restored swscale and updated opengl fx spec

This commit is contained in:
itsmattkc
2018-08-20 12:36:53 +10:00
parent 951be4d223
commit b06ab214ea
26 changed files with 395 additions and 309 deletions
+117 -79
View File
@@ -136,79 +136,111 @@ bool Effect::is_enabled() {
return container->enabled_check->isChecked();
}
void Effect::load(QXmlStreamReader& stream) {
stream.readNext();
/*for (int i=0;i<rows.size();i++) {
EffectRow* row = rows.at(i);
while (!stream->atEnd() && !(stream->name() == "effect" && stream->isEndElement())) {
stream->readNext();
if (stream->name() == "row" && stream->isStartElement()) {
for (int j=0;j<row->fieldCount();j++) {
EffectField* field = row->field(j);
while (!stream->atEnd() && !(stream->name() == "effect" && stream->isEndElement())) {
stream->readNext();
if (stream->name() == "field" && stream->isStartElement()) {
// read all keyframes
QVector<EffectKeyframe> keys;
while (!stream->atEnd() && (stream->name() == "field" && stream->isEndElement())) {
stream->readNext();
if (stream->name() == "key" && stream->isStartElement()) {
EffectKeyframe kf;
for (int k=0;k<stream->attributes().size();k++) {
const QXmlStreamAttribute& attr = stream->attributes().at(k);
if (attr.name() == "frame") {
kf.frame = attr.value().toLong();
} else if (attr.name() == "type") {
kf.type = attr.value().toInt();
} else if (attr.name() == "value") {
switch (field->type) {
case EFFECT_FIELD_DOUBLE:
kf.data = attr.value().toDouble();
break;
case EFFECT_FIELD_COLOR:
{
kf.data = QColor(attr.value().toString());
}
break;
case EFFECT_FIELD_STRING:
kf.data = attr.value().toString();
break;
case EFFECT_FIELD_BOOL:
kf.data = (stream->text() == "1");
break;
case EFFECT_FIELD_COMBO:
kf.data = (stream->text().toInt());
break;
case EFFECT_FIELD_FONT:
kf.data = (stream->text().toString());
break;
}
}
}
keys.append(kf);
}
}
field->keyframes = keys;
break;
}
}
}
break;
}
}
}*/
QVariant load_data_from_string(int type, const QString& string) {
switch (type) {
case EFFECT_FIELD_DOUBLE: return string.toDouble(); break;
case EFFECT_FIELD_COLOR: return QColor(string); break;
case EFFECT_FIELD_STRING: return string; break;
case EFFECT_FIELD_BOOL: return (string == "1"); break;
case EFFECT_FIELD_COMBO: return string.toInt(); break;
case EFFECT_FIELD_FONT: return string; break;
}
return QVariant();
}
QString save_data(int type, const QVariant& data) {
switch (type) {
case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble()); break;
case EFFECT_FIELD_COLOR: return data.value<QColor>().name(); break;
case EFFECT_FIELD_STRING: return data.toString(); break;
case EFFECT_FIELD_BOOL: return QString::number(data.toBool()); break;
case EFFECT_FIELD_COMBO: return QString::number(data.toInt()); break;
case EFFECT_FIELD_FONT: return data.toString(); break;
}
return QString();
QString save_data_to_string(int type, const QVariant& data) {
switch (type) {
case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble()); break;
case EFFECT_FIELD_COLOR: return data.value<QColor>().name(); break;
case EFFECT_FIELD_STRING: return data.toString(); break;
case EFFECT_FIELD_BOOL: return QString::number(data.toBool()); break;
case EFFECT_FIELD_COMBO: return QString::number(data.toInt()); break;
case EFFECT_FIELD_FONT: return data.toString(); break;
}
return QString();
}
void Effect::load(QXmlStreamReader& stream) {
int row_count = 0;
while (!stream.atEnd() && !(stream.name() == "effect" && stream.isEndElement())) {
stream.readNext();
if (stream.name() == "row" && stream.isStartElement()) {
if (row_count < rows.size()) {
EffectRow* row = rows.at(row_count);
int field_count = 0;
while (!stream.atEnd() && !(stream.name() == "row" && stream.isEndElement())) {
stream.readNext();
// read keyframes
if (stream.name() == "keyframes" && stream.isStartElement()) {
for (int k=0;k<stream.attributes().size();k++) {
const QXmlStreamAttribute& attr = stream.attributes().at(k);
if (attr.name() == "enabled") {
row->keyframing = (attr.value() == "1");
break;
}
}
if (row->keyframing) {
stream.readNext();
while (!stream.atEnd() && !(stream.name() == "keyframes" && stream.isEndElement())) {
if (stream.name() == "key" && stream.isStartElement()) {
long keyframe_frame;
int keyframe_type;
for (int k=0;k<stream.attributes().size();k++) {
const QXmlStreamAttribute& attr = stream.attributes().at(k);
if (attr.name() == "frame") {
keyframe_frame = attr.value().toLong();
} else if (attr.name() == "type") {
keyframe_type = attr.value().toInt();
}
}
row->keyframe_times.append(keyframe_frame);
row->keyframe_types.append(keyframe_type);
}
stream.readNext();
}
}
stream.readNext();
}
// read field
if (stream.name() == "field" && stream.isStartElement()) {
if (field_count < row->fieldCount()) {
EffectField* field = row->field(field_count);
for (int k=0;k<stream.attributes().size();k++) {
const QXmlStreamAttribute& attr = stream.attributes().at(k);
if (attr.name() == "value") {
field->set_current_data(load_data_from_string(field->type, attr.value().toString()));
break;
}
}
while (!stream.atEnd() && !(stream.name() == "field" && stream.isEndElement())) {
stream.readNext();
// read all keyframes
if (stream.name() == "key" && stream.isStartElement()) {
stream.readNext();
field->keyframe_data.append(load_data_from_string(field->type, stream.text().toString()));
}
}
} else {
qDebug() << "[ERROR] Too many fields for effect" << id << "row" << row_count << ". Project might be corrupt. (Got" << field_count << ", expected <" << row->fieldCount()-1 << ")";
}
field_count++;
break;
}
}
} else {
qDebug() << "[ERROR] Too many rows for effect" << id << ". Project might be corrupt. (Got" << row_count << ", expected <" << rows.size()-1 << ")";
}
row_count++;
}
}
}
void Effect::save(QXmlStreamWriter& stream) {
@@ -227,9 +259,9 @@ void Effect::save(QXmlStreamWriter& stream) {
for (int j=0;j<row->fieldCount();j++) {
EffectField* field = row->field(j);
stream.writeStartElement("field"); // field
stream.writeAttribute("value", save_data(field->type, field->get_current_data()));
stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data()));
for (int k=0;k<field->keyframe_data.size();k++) {
stream.writeTextElement("key", save_data(field->type, field->keyframe_data.at(k)));
stream.writeTextElement("key", save_data_to_string(field->type, field->keyframe_data.at(k)));
}
stream.writeEndElement(); // field
}
@@ -243,8 +275,8 @@ Effect* Effect::copy(Clip* c) {
return copy;
}
void Effect::process_image(long, QImage&) {}
void Effect::process_gl(long, QOpenGLShaderProgram&, int*, int*) {}
void Effect::process_image(long, uint8_t*, int, int) {}
void Effect::process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords) {}
void Effect::process_audio(uint8_t*, int) {}
/* Effect Row Definitions */
@@ -261,6 +293,7 @@ EffectRow::EffectRow(Effect *parent, QGridLayout *uilayout, const QString &n, in
// DEBUG STARTS
QPushButton* nkf = new QPushButton();
nkf->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
connect(nkf, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_now()));
ui->addWidget(nkf, row, 5);
// DEBUG ENDS
@@ -337,7 +370,7 @@ EffectField::EffectField(EffectRow *parent, int t) : parent_row(parent), type(t)
QTextEdit* edit = new QTextEdit();
edit->setUndoRedoEnabled(true);
ui_element = edit;
connect(edit, SIGNAL(textChanged()), this, SLOT(uiElementChange()));
connect(edit->document(), SIGNAL(contentsChanged()), this, SLOT(uiElementChange()));
}
break;
case EFFECT_FIELD_BOOL:
@@ -432,9 +465,14 @@ void EffectField::get_keyframe_data(long frame, int* before, int* after, double*
*before = after_keyframe_index;
*after = after_keyframe_index;
}
} else {
*before = before_keyframe_index;
*after = before_keyframe_index;
} else {
if (before_keyframe_index > -1) {
*before = before_keyframe_index;
*after = before_keyframe_index;
} else {
*before = after_keyframe_time;
*after = after_keyframe_time;
}
}
}
+22 -2
View File
@@ -53,6 +53,26 @@ Effect* create_effect(int effect_id, Clip* c);
#define EFFECT_KEYFRAME_HOLD 1
#define EFFECT_KEYFRAME_BEZIER 2
struct GLTextureCoords {
int vertexTopLeftX;
int vertexTopLeftY;
int vertexTopRightX;
int vertexTopRightY;
int vertexBottomLeftX;
int vertexBottomLeftY;
int vertexBottomRightX;
int vertexBottomRightY;
double textureTopLeftX;
double textureTopLeftY;
double textureTopRightX;
double textureTopRightY;
double textureBottomRightX;
double textureBottomRightY;
double textureBottomLeftX;
double textureBottomLeftY;
};
class EffectField : public QObject {
Q_OBJECT
public:
@@ -163,8 +183,8 @@ public:
const char* ffmpeg_filter;
virtual void process_image(long frame, QImage& img);
virtual void process_gl(long frame, QOpenGLShaderProgram& shader_prog, int* anchor_x, int* anchor_y);
virtual void process_image(long frame, uint8_t* data, int width, int height);
virtual void process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords);
virtual void process_audio(quint8* samples, int nb_bytes);
public slots:
void field_changed();
+5 -9
View File
@@ -8,7 +8,7 @@
#include <QXmlStreamWriter>
#include <QXmlStreamAttributes>
InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_EFFECT), vert_shader(QOpenGLShader::Vertex), frag_shader(QOpenGLShader::Fragment) {
InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_EFFECT) {
enable_opengl = true;
EffectRow* amount_row = add_row("Amount:");
@@ -22,12 +22,8 @@ InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_
connect(amount_val, SIGNAL(changed()), this, SLOT(field_changed()));
}
void InvertEffect::process_gl(long p, QOpenGLShaderProgram& shader_prog, int* anchor_x, int* anchor_y) {
double value = amount_val->get_double_value(p)*0.01;
vert_shader.compileSourceCode("varying vec2 vTexCoord;\nvoid main() {\n\tvTexCoord = gl_MultiTexCoord0.xy;\n\tgl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n}");
frag_shader.compileSourceCode("uniform sampler2D myTexture;\nvarying vec2 vTexCoord;\nvoid main(void) {\n\tvec4 textureColor = texture2D(myTexture, vTexCoord);\n\tgl_FragColor = vec4(textureColor.r+((1.0-textureColor.r-textureColor.r)*" + QString::number(value, 'f', 2) + "), textureColor.g+((1.0-textureColor.g-textureColor.g)*" + QString::number(value, 'f', 2) + "), textureColor.b+((1.0-textureColor.b-textureColor.b)*" + QString::number(value, 'f', 2) + "), 1);\n}");
shader_prog.addShader(&vert_shader);
shader_prog.addShader(&frag_shader);
void InvertEffect::process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords&) {
double value = amount_val->get_double_value(frame)*0.01;
shaders.addShaderFromSourceCode(QOpenGLShader::Vertex, "varying vec2 vTexCoord;\nvoid main() {\n\tvTexCoord = gl_MultiTexCoord0.xy;\n\tgl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n}");
shaders.addShaderFromSourceCode(QOpenGLShader::Fragment, "uniform sampler2D myTexture;\nvarying vec2 vTexCoord;\nvoid main(void) {\n\tvec4 textureColor = texture2D(myTexture, vTexCoord);\n\tgl_FragColor = vec4(textureColor.r+((1.0-textureColor.r-textureColor.r)*" + QString::number(value, 'f', 2) + "), textureColor.g+((1.0-textureColor.g-textureColor.g)*" + QString::number(value, 'f', 2) + "), textureColor.b+((1.0-textureColor.b-textureColor.b)*" + QString::number(value, 'f', 2) + "), 1);\n}");
}
+2 -5
View File
@@ -9,12 +9,9 @@ class InvertEffect : public Effect {
Q_OBJECT
public:
InvertEffect(Clip* c);
void process_gl(long p, QOpenGLShaderProgram& shader_prog, int *anchor_x, int *anchor_y);
EffectField* amount_val;
void process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords);
private:
QOpenGLShader vert_shader;
QOpenGLShader frag_shader;
EffectField* amount_val;
};
#endif // INVERTEFFECT_H
+3 -3
View File
@@ -49,9 +49,9 @@ void ShakeEffect::refresh() {
}
}
void ShakeEffect::process_gl(long p, QOpenGLShaderProgram&, int*, int*) {
void ShakeEffect::process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords) {
if (shake_progress > shake_limit) {
double ival = intensity_val->get_double_value(p);
double ival = intensity_val->get_double_value(frame);
if ((int)ival > 0) {
prev_x = next_x;
prev_y = next_y;
@@ -81,7 +81,7 @@ void ShakeEffect::process_gl(long p, QOpenGLShaderProgram&, int*, int*) {
offset_x = 0;
offset_y = 0;
}
double rot_val = rotation_val->get_double_value(p);
double rot_val = rotation_val->get_double_value(frame);
if ((int)rot_val > 0) {
prev_rot = next_rot;
next_rot = (qrand() % (int) (rot_val * 2)) - rot_val;
+1 -1
View File
@@ -7,7 +7,7 @@ class ShakeEffect : public Effect {
Q_OBJECT
public:
ShakeEffect(Clip* c);
void process_gl(long p, QOpenGLShaderProgram& shader_prog, int* anchor_x, int* anchor_y);
void process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords);
EffectField* intensity_val;
EffectField* rotation_val;
+7 -2
View File
@@ -16,7 +16,7 @@
#define SMPTE_LOWER_BARS 4
SolidEffect::SolidEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SOLID_EFFECT) {
enable_image = true;
enable_opengl = true;
solid_type = add_row("Type:")->add_field(EFFECT_FIELD_COMBO);
solid_type->add_combo_item("Solid Color", SOLID_TYPE_COLOR);
@@ -40,7 +40,12 @@ void SolidEffect::enable_color() {
solid_color_field->set_enabled(solid_type->get_combo_data(-1) == SOLID_TYPE_COLOR);
}
void SolidEffect::process_image(long frame, QImage& img) {
void SolidEffect::process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords) {
}
void SolidEffect::process_image(long frame, uint8_t* data, int w, int h) {
QImage img(data, w, h, QImage::Format_RGBA8888); // create QImage wrapper
QPainter p(&img);
int width = img.width();
int height = img.height();
+2 -1
View File
@@ -10,7 +10,8 @@ public:
EffectField* solid_type;
EffectField* solid_color_field;
EffectField* opacity_field;
void process_image(long p, QImage &img);
void process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords);
void process_image(long p, uint8_t* data, int width, int height);
private slots:
void enable_color();
};
+2 -1
View File
@@ -242,7 +242,8 @@ void blurred2(QImage& result, const QRect& rect, int radius, bool alphaOnly = fa
}
}
void TextEffect::process_image(long frame, QImage& img) {
void TextEffect::process_image(long frame, uint8_t* data, int w, int h) {
QImage img(data, w, h, QImage::Format_RGBA8888);
QPainter p(&img);
p.setRenderHint(QPainter::Antialiasing);
int width = img.width();
+1 -1
View File
@@ -9,7 +9,7 @@ class TextEffect : public Effect {
Q_OBJECT
public:
TextEffect(Clip* c);
void process_image(long frame, QImage& img);
void process_image(long frame, uint8_t* data, int width, int height);
EffectField* text_val;
EffectField* size_val;
+11 -3
View File
@@ -121,13 +121,21 @@ void TransformEffect::toggle_uniform_scale(bool enabled) {
scale_y->set_enabled(!enabled);
}
void TransformEffect::process_gl(long frame, QOpenGLShaderProgram&, int* anchor_x, int* anchor_y) {
void TransformEffect::process_gl(long frame, QOpenGLShaderProgram&, GLTextureCoords& coords) {
// position
glTranslatef(position_x->get_double_value(frame)-(parent_clip->sequence->width/2), position_y->get_double_value(frame)-(parent_clip->sequence->height/2), 0);
// anchor point
*anchor_x += (anchor_x_box->get_double_value(frame)-default_anchor_x);
*anchor_y += (anchor_y_box->get_double_value(frame)-default_anchor_y);
int anchor_x_offset = (anchor_x_box->get_double_value(frame)-default_anchor_x);
int anchor_y_offset = (anchor_y_box->get_double_value(frame)-default_anchor_y);
coords.vertexTopLeftX += anchor_x_offset;
coords.vertexTopRightX += anchor_x_offset;
coords.vertexBottomLeftX += anchor_x_offset;
coords.vertexBottomRightX += anchor_x_offset;
coords.vertexTopLeftY += anchor_y_offset;
coords.vertexTopRightY += anchor_y_offset;
coords.vertexBottomLeftY += anchor_y_offset;
coords.vertexBottomRightY += anchor_y_offset;
// rotation
glRotatef(rotation->get_double_value(frame), 0, 0, 1);
+1 -1
View File
@@ -8,7 +8,7 @@ class TransformEffect : public Effect {
public:
TransformEffect(Clip* c);
void refresh();
void process_gl(long p, QOpenGLShaderProgram& shader_prog, int* anchor_x, int* anchor_y);
void process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords);
EffectField* position_x;
EffectField* position_y;
+1 -3
View File
@@ -3,14 +3,12 @@
extern "C" {
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
}
int main(int argc, char *argv[])
{
// init ffmpeg subsystem
av_register_all();
avfilter_register_all();
av_register_all();
QApplication a(argc, argv);
MainWindow w;
+3 -3
View File
@@ -134,16 +134,16 @@ win32 {
LIBS += -L../ffmpeg/lib -lopengl32
INCLUDEPATH = ../ffmpeg/include
RC_FILE = icons/win.rc
LIBS += -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample
LIBS += -lavutil -lavformat -lavcodec -lswscale -lswresample
}
mac {
LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample
LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lswscale -lswresample
INCLUDEPATH = /usr/local/include
}
linux {
LIBS += -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample
LIBS += -lavutil -lavformat -lavcodec -lswscale -lswresample
}
RESOURCES += \
+3
View File
@@ -35,7 +35,10 @@ EffectControls::EffectControls(QWidget *parent) :
ui->effects_area->keyframe_area = ui->keyframeView;
ui->effects_area->header = ui->headers;
ui->keyframeView->header = ui->headers;
connect(ui->keyframeScroller->verticalScrollBar(), SIGNAL(valueChanged(int)), ui->scrollArea->verticalScrollBar(), SLOT(setValue(int)));
connect(ui->keyframeScroller->horizontalScrollBar(), SIGNAL(valueChanged(int)), ui->keyframeHeaderScroller->horizontalScrollBar(), SLOT(setValue(int)));
}
EffectControls::~EffectControls() {
+144 -62
View File
@@ -23,7 +23,7 @@
<string>Effect Controls</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout_7">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>0</number>
</property>
@@ -40,13 +40,10 @@
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter_2">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Expanding">
@@ -421,71 +418,156 @@
</layout>
</widget>
</widget>
<widget class="QScrollArea" name="keyframeScroller">
<widget class="QWidget" name="keyframeArea" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>376</width>
<height>475</height>
</rect>
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="TimelineHeader" name="headers" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>15</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="keyframeHeaderScroller">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>15</height>
</size>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>390</width>
<height>16</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="TimelineHeader" name="headers" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>15</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="KeyframeView" name="keyframeView" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QScrollArea" name="keyframeScroller">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>378</width>
<height>462</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="KeyframeView" name="keyframeView" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
+16 -10
View File
@@ -373,7 +373,7 @@
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QScrollArea" name="videoScrollArea">
<widget class="ScrollArea" name="videoScrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
@@ -395,7 +395,7 @@
<x>0</x>
<y>0</y>
<width>807</width>
<height>277</height>
<height>163</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -424,7 +424,7 @@
</layout>
</widget>
</widget>
<widget class="QScrollArea" name="audioScrollArea">
<widget class="ScrollArea" name="audioScrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
@@ -446,7 +446,7 @@
<x>0</x>
<y>0</y>
<width>807</width>
<height>263</height>
<height>377</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
@@ -494,24 +494,30 @@
</widget>
</widget>
<customwidgets>
<customwidget>
<class>TimelineWidget</class>
<extends>QWidget</extends>
<header>ui/timelinewidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>TimelineHeader</class>
<extends>QWidget</extends>
<header>ui/timelineheader.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>TimelineWidget</class>
<extends>QWidget</extends>
<header>ui/timelinewidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AudioMonitor</class>
<extends>QWidget</extends>
<header>ui/audiomonitor.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ScrollArea</class>
<extends>QScrollArea</extends>
<header>ui/scrollarea.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../icons/icons.qrc"/>
+21 -88
View File
@@ -12,10 +12,6 @@
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
@@ -188,55 +184,14 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
int i = 0;
/* swscale solution - might be faster? but AVFilter solution is more "future-proof"
if (!c->reached_end) {
while (i < c->cache_size) {
time = QDateTime::currentMSecsSinceEpoch();
retrieve_next_frame_raw_data(c, cache->frames[i]);
qDebug() << (QDateTime::currentMSecsSinceEpoch() - time);
if (c->reached_end) break;
i++;
}
}
*/
/* AVFilter solution - not definitely slower, may allow for cool things later */
if (!c->reached_end) {
while (i < c->cache_size) {
av_frame_unref(cache->frames[i]);
int ret = (c->filter_graph == NULL) ? AVERROR(EAGAIN) : av_buffersink_get_frame(c->buffersink_ctx, cache->frames[i]);
if (ret < 0) {
if (ret == AVERROR(EAGAIN)) {
ret = retrieve_next_frame(c, c->frame);
if (ret >= 0) {
if ((ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, c->frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
qDebug() << "[ERROR] Could not feed filtergraph -" << ret;
error = true;
break;
}
} else {
if (ret == AVERROR_EOF) {
c->reached_end = true;
} else {
qDebug() << "[WARNING] Raw frame data could not be retrieved." << ret;
error = true;
}
break;
}
} else {
if (ret != AVERROR_EOF) {
qDebug() << "[ERROR] Could not pull from filtergraph";
error = true;
}
break;
}
} else {
i++;
}
}
}
cache->write_count = i;
if (!error) {
@@ -344,10 +299,10 @@ void open_clip_worker(Clip* clip) {
if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
// set up swscale context - primarily used for colorspace conversion
// as "scaling" is actually done by OpenGL
// int dstW = ceil(clip->stream->codecpar->width/2)*2;
// int dstH = ceil(clip->stream->codecpar->height/2)*2;
int dstW = ceil(clip->stream->codecpar->width/2)*2;
int dstH = ceil(clip->stream->codecpar->height/2)*2;
/*clip->sws_ctx = sws_getContext(
clip->sws_ctx = sws_getContext(
clip->stream->codecpar->width,
clip->stream->codecpar->height,
static_cast<AVPixelFormat>(clip->stream->codecpar->format),
@@ -358,7 +313,7 @@ void open_clip_worker(Clip* clip) {
NULL,
NULL,
NULL
);*/
);
// create memory cache for video
clip->cache_size = (ms->infinite_length) ? 1 : ceil(av_q2d(av_guess_frame_rate(clip->formatCtx, clip->stream, NULL))/4); // cache is half a second in total
@@ -368,48 +323,28 @@ void open_clip_worker(Clip* clip) {
clip->cache_B.frames = new AVFrame* [clip->cache_size];
for (int i=0;i<clip->cache_size;i++) {
clip->cache_A.frames[i] = av_frame_alloc();
av_frame_make_writable(clip->cache_A.frames[i]);
clip->cache_A.frames[i]->width = dstW;
clip->cache_A.frames[i]->height = dstH;
clip->cache_A.frames[i]->format = dest_format;
if (av_frame_get_buffer(clip->cache_A.frames[i], 0)) {
qDebug() << "[ERROR] Could not allocate buffer for sws_frame";
}
clip->cache_A.frames[i]->linesize[0] = dstW*4;
clip->cache_B.frames[i] = av_frame_alloc();
av_frame_make_writable(clip->cache_B.frames[i]);
clip->cache_B.frames[i]->width = dstW;
clip->cache_B.frames[i]->height = dstH;
clip->cache_B.frames[i]->format = dest_format;
if (av_frame_get_buffer(clip->cache_B.frames[i], 0)) {
qDebug() << "[ERROR] Could not allocate buffer for sws_frame";
}
clip->cache_B.frames[i]->linesize[0] = dstW*4;
}
clip->comp_frame_size = clip->stream->codecpar->width * clip->stream->codecpar->height * 4;
clip->comp_frame = new uchar[clip->comp_frame_size];
// alloc temporary scale frame
/*clip->sws_frame = av_frame_alloc();
av_frame_make_writable(clip->sws_frame);
clip->sws_frame->width = dstW;
clip->sws_frame->height = dstH;
clip->sws_frame->format = dest_format;
if (av_frame_get_buffer(clip->sws_frame, 0)) {
qDebug() << "[ERROR] Could not allocate buffer for sws_frame";
}
clip->sws_frame->linesize[0] = clip->stream->codecpar->width*4;*/
clip->filter_graph = avfilter_graph_alloc();
char args[512];
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
clip->stream->codecpar->width, clip->stream->codecpar->height, clip->stream->codecpar->format,
clip->stream->time_base.num, clip->stream->time_base.den,
clip->stream->codecpar->sample_aspect_ratio.num, clip->stream->codecpar->sample_aspect_ratio.den);
avfilter_graph_create_filter(&clip->buffersrc_ctx, avfilter_get_by_name("buffer"), "in", args, NULL, clip->filter_graph);
avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("buffersink"), "out", NULL, NULL, clip->filter_graph);
enum AVPixelFormat pix_fmts[] = { static_cast<AVPixelFormat>(dest_format), AV_PIX_FMT_NONE };
av_opt_set_int_list(clip->buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0);
avfilter_graph_config(clip->filter_graph, NULL);
/* old AVFilter code, looks like it'll be unusable
AVFilterContext* gblur_ctx;
avfilter_graph_create_filter(&gblur_ctx, avfilter_get_by_name("gblur"), "ol_gblur", "sigma=20:steps=1", NULL, c->filter_graph);
avfilter_graph_create_filter(&gblur_ctx, avfilter_get_by_name("negate"), "ol_gblur", NULL, NULL, clip->filter_graph);
avfilter_graph_create_filter(&gblur_ctx, avfilter_get_by_name("null"), "ol_gblur", NULL, NULL, clip->filter_graph);
avfilter_link(clip->buffersrc_ctx, 0, gblur_ctx, 0);
avfilter_link(gblur_ctx, 0, clip->buffersink_ctx, 0);
avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0);
*/
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
// if FFmpeg can't pick up the channel layout (usually WAV), assume
// based on channel count (doesn't support surround sound sources yet)
@@ -480,8 +415,6 @@ void close_clip_worker(Clip* clip) {
// sws_freeContext(clip->sws_ctx);
// TODO will eventually be in audio too
// av_frame_free(&clip->sws_frame);
avfilter_graph_free(&clip->filter_graph);
delete [] clip->comp_frame;
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
swr_free(&clip->swr_ctx);
+3 -3
View File
@@ -166,11 +166,11 @@ bool get_clip_frame(Clip* c, long playhead) {
c->texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8);
}
memcpy(c->comp_frame, current_frame->data[0], c->comp_frame_size);
QImage img(c->comp_frame, current_frame->width, current_frame->height, QImage::Format_RGBA8888);
for (int i=0;i<c->effects.size();i++) {
if (c->effects.at(i)->enable_image) {
c->effects.at(i)->process_image(sequence_clip_time, img);
c->effects.at(i)->process_image(sequence_clip_time, c->comp_frame, current_frame->width, current_frame->height);
}
}
@@ -248,7 +248,7 @@ void retrieve_next_frame_raw_data(Clip* c, AVFrame* output) {
int ret = retrieve_next_frame(c, c->frame);
if (ret >= 0) {
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
// sws_scale(c->sws_ctx, c->frame->data, c->frame->linesize, 0, c->stream->codecpar->height, output->data, output->linesize);
sws_scale(c->sws_ctx, c->frame->data, c->frame->linesize, 0, c->stream->codecpar->height, output->data, output->linesize);
// output->pts = c->frame->best_effort_timestamp;
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
output->pts = c->frame->pts;
+1 -2
View File
@@ -25,8 +25,7 @@ Clip::Clip(Sequence* s) :
media(NULL),
opening_transition(NULL),
closing_transition(NULL),
pkt(new AVPacket()),
filter_graph(NULL),
pkt(new AVPacket()),
replaced(false),
texture(NULL)
{
+1 -7
View File
@@ -18,8 +18,6 @@ struct AVCodec;
struct AVCodecContext;
struct AVFrame;
struct AVPacket;
struct AVFilterGraph;
struct AVFilterContext;
struct SwsContext;
struct SwrContext;
class QOpenGLTexture;
@@ -81,11 +79,7 @@ struct Clip
AVFrame* frame;
uchar* comp_frame;
int comp_frame_size;
// ffmpeg filters
AVFilterGraph* filter_graph;
AVFilterContext* buffersink_ctx;
AVFilterContext* buffersrc_ctx;
SwsContext* sws_ctx;
bool pkt_written;
bool reached_end;
-4
View File
@@ -34,10 +34,6 @@ QUndoStack undo_stack;
#define TA_MODIFY_TRANSITION 11
#define TA_DELETE_TRANSITION 12
extern "C" {
#include "libavfilter/avfilter.h"
}
TimelineAction::TimelineAction() :
done(false),
change_seq(false),
+5 -3
View File
@@ -6,6 +6,7 @@
#include "panels/effectcontrols.h"
#include "project/clip.h"
#include "panels/timeline.h"
#include "ui/timelineheader.h"
#include <QLabel>
#include <QMouseEvent>
@@ -21,7 +22,9 @@ KeyframeView::KeyframeView(QWidget *parent) : QWidget(parent), mousedown(false),
void KeyframeView::paintEvent(QPaintEvent*) {
QPainter p(this);
setMinimumWidth(getScreenPointFromFrame(panel_effect_controls->zoom, visible_out - visible_in));
int width = getScreenPointFromFrame(panel_effect_controls->zoom, visible_out - visible_in);
setMinimumWidth(width);
header->setMinimumWidth(width);
rowY.clear();
rows.clear();
@@ -37,8 +40,7 @@ void KeyframeView::paintEvent(QPaintEvent*) {
int keyframe_y = label->y() + (label->height()>>1) + mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() - e->container->title_bar->height();
for (int k=0;k<row->keyframe_times.size();k++) {
bool keyframe_selected = false;
for (int l=0;l<selected_rows.size();l++) {
// qDebug() << selected_rows.at(l) << rows.size() << selected_keyframes.at(l) << k;
for (int l=0;l<selected_rows.size();l++) {
if (selected_rows.at(l) == rows.size() && selected_keyframes.at(l) == k) {
keyframe_selected = true;
break;
+3
View File
@@ -7,6 +7,7 @@
struct Clip;
class Effect;
class EffectRow;
class TimelineHeader;
class KeyframeView : public QWidget {
Q_OBJECT
@@ -14,6 +15,8 @@ public:
KeyframeView(QWidget* parent = 0);
QVector<Effect*> effects;
TimelineHeader* header;
long visible_in;
long visible_out;
private:
+1 -1
View File
@@ -29,7 +29,7 @@ void LabelSlider::set_value(double v, bool userSet) {
internal_value = v;
}
setText(QString::number(internal_value));
setText(QString::number(internal_value, 'f', 1));
if (userSet) emit valueChanged();
}
}
+19 -15
View File
@@ -171,13 +171,20 @@ void ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio) {
int half_height = sequence->height/2;
if (flip) half_height = -half_height;
glOrtho(-half_width, half_width, half_height, -half_height, -1, 1);
int anchor_x = ms->video_width/2;
int anchor_y = ms->video_height/2;
GLTextureCoords coords;
coords.vertexTopLeftX = coords.vertexBottomLeftX = -ms->video_width/2;
coords.vertexTopLeftY = coords.vertexTopRightY = -ms->video_height/2;
coords.vertexTopRightX = coords.vertexBottomRightX = ms->video_width/2;
coords.vertexBottomLeftY = coords.vertexBottomRightY = ms->video_height/2;
coords.textureTopLeftY = coords.textureTopRightY = coords.textureTopLeftX = coords.textureBottomLeftX = 0;
coords.textureBottomLeftY = coords.textureBottomRightY = coords.textureTopRightX = coords.textureBottomRightX = 1.0;
QOpenGLShaderProgram shader;
for (int j=0;j<c->effects.size();j++) {
if (c->effects.at(j)->enable_opengl && c->effects.at(j)->is_enabled()) c->effects.at(j)->process_gl(panel_timeline->playhead-c->timeline_in+c->clip_in, shader, &anchor_x, &anchor_y);
if (c->effects.at(j)->enable_opengl && c->effects.at(j)->is_enabled())
c->effects.at(j)->process_gl(panel_timeline->playhead-c->timeline_in+c->clip_in, shader, coords);
}
if (c->opening_transition != NULL) {
@@ -192,10 +199,7 @@ void ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio) {
if (transition_progress < c->closing_transition->length) {
c->closing_transition->process_transition((double)transition_progress/(double)c->closing_transition->length);
}
}
int anchor_right = ms->video_width - anchor_x;
int anchor_bottom = ms->video_height - anchor_y;
}
bool use_gl_shaders = shader.link();
if (use_gl_shaders) shader.bind();
@@ -203,14 +207,14 @@ void ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio) {
c->texture->bind();
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0);
glVertex2f(-anchor_x, -anchor_y);
glTexCoord2f(1.0, 0.0);
glVertex2f(anchor_right, -anchor_y);
glTexCoord2f(1.0, 1.0);
glVertex2f(anchor_right, anchor_bottom);
glTexCoord2f(0.0, 1.0);
glVertex2f(-anchor_x, anchor_bottom);
glTexCoord2f(coords.textureTopLeftX, coords.textureTopLeftY); // top left
glVertex2f(coords.vertexTopLeftX, coords.vertexTopLeftY); // top left
glTexCoord2f(coords.textureTopRightX, coords.textureTopRightY); // top right
glVertex2f(coords.vertexTopRightX, coords.vertexTopRightY); // top right
glTexCoord2f(coords.textureBottomRightX, coords.textureBottomRightY); // bottom right
glVertex2f(coords.vertexBottomRightX, coords.vertexBottomRightY); // bottom right
glTexCoord2f(coords.textureBottomLeftX, coords.textureBottomLeftY); // bottom left
glVertex2f(coords.vertexBottomLeftX, coords.vertexBottomLeftY); // bottom left
glEnd();
c->texture->release();