improved effects
This commit is contained in:
+7
-2
@@ -147,7 +147,11 @@ void Effect::field_changed() {
|
||||
}
|
||||
|
||||
bool Effect::is_enabled() {
|
||||
return container->enabled_check->isChecked();
|
||||
return container->enabled_check->isChecked();
|
||||
}
|
||||
|
||||
void Effect::set_enabled(bool b) {
|
||||
container->enabled_check->setChecked(b);
|
||||
}
|
||||
|
||||
QVariant load_data_from_string(int type, const QString& string) {
|
||||
@@ -290,8 +294,9 @@ Effect* Effect::copy(Clip* c) {
|
||||
}
|
||||
|
||||
void Effect::process_image(double, uint8_t*, int, int) {}
|
||||
void Effect::process_gl(double, QOpenGLShaderProgram&, GLTextureCoords&) {}
|
||||
void Effect::process_gl(double, GLTextureCoords&) {}
|
||||
void Effect::process_audio(double, double, quint8*, int, int) {}
|
||||
void Effect::clean_gl() {}
|
||||
|
||||
/* Effect Row Definitions */
|
||||
|
||||
|
||||
+3
-1
@@ -180,6 +180,7 @@ public:
|
||||
int row_count();
|
||||
|
||||
bool is_enabled();
|
||||
void set_enabled(bool b);
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
@@ -195,7 +196,8 @@ public:
|
||||
const char* ffmpeg_filter;
|
||||
|
||||
virtual void process_image(double timecode, uint8_t* data, int width, int height);
|
||||
virtual void process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords& coords);
|
||||
virtual void process_gl(double timecode, GLTextureCoords& coords);
|
||||
virtual void clean_gl();
|
||||
virtual void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
public slots:
|
||||
void field_changed();
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
#include "chromakeyeffect.h"
|
||||
|
||||
ChromaKeyEffect::ChromaKeyEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_CHROMAKEY_EFFECT) {
|
||||
ChromaKeyEffect::ChromaKeyEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_CHROMAKEY_EFFECT), vert(QOpenGLShader::Vertex), frag(QOpenGLShader::Fragment) {
|
||||
enable_opengl = true;
|
||||
|
||||
color_field = add_row("Color:")->add_field(EFFECT_FIELD_COLOR);
|
||||
tolerance_field = add_row("Tolerance:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
|
||||
color_field->set_color_value(Qt::green);
|
||||
tolerance_field->set_double_default_value(50);
|
||||
tolerance_field->set_double_default_value(10);
|
||||
|
||||
tolerance_field->set_double_minimum_value(0);
|
||||
tolerance_field->set_double_maximum_value(100);
|
||||
|
||||
vert.compileSourceCode("varying vec2 vTexCoord;\nvoid main() {\n\tvTexCoord = gl_MultiTexCoord0.xy;\n\tgl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n}");
|
||||
frag.compileSourceCode("uniform vec4 keyColor;\nuniform float threshold;\nuniform sampler2D myTexture;\nvarying vec2 vTexCoord;\nvoid main(void) {\n\tvec4 textureColor = texture2D(myTexture, vTexCoord);\nfloat diff = length(keyColor - textureColor);\nif (diff < threshold) {gl_FragColor = vec4(0, 0, 0, 0);} else {gl_FragColor = textureColor;}\n}");
|
||||
program.addShader(&vert);
|
||||
program.addShader(&frag);
|
||||
program.link();
|
||||
|
||||
connect(color_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(tolerance_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
}
|
||||
|
||||
void ChromaKeyEffect::process_gl(double timecode, QOpenGLShaderProgram &shaders, GLTextureCoords&) {
|
||||
void ChromaKeyEffect::process_gl(double timecode, GLTextureCoords&) {
|
||||
program.bind();
|
||||
program.setUniformValue("keyColor", color_field->get_color_value(timecode));
|
||||
program.setUniformValue("threshold", (GLfloat) (tolerance_field->get_double_value(timecode)*0.01));
|
||||
}
|
||||
|
||||
void ChromaKeyEffect::clean_gl() {
|
||||
program.release();
|
||||
}
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
class ChromaKeyEffect : public Effect {
|
||||
public:
|
||||
ChromaKeyEffect(Clip* c);
|
||||
void process_gl(double timecode, QOpenGLShaderProgram &shaders, GLTextureCoords &coords);
|
||||
void process_gl(double timecode, GLTextureCoords &coords);
|
||||
void clean_gl();
|
||||
private:
|
||||
EffectField* color_field;
|
||||
EffectField* tolerance_field;
|
||||
QOpenGLShaderProgram program;
|
||||
QOpenGLShader vert;
|
||||
QOpenGLShader frag;
|
||||
};
|
||||
|
||||
#endif // CHROMAKEYEFFECT_H
|
||||
|
||||
@@ -23,7 +23,7 @@ CropEffect::CropEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_CROP_EFFECT
|
||||
connect(bottom_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
}
|
||||
|
||||
void CropEffect::process_gl(double timecode, QOpenGLShaderProgram&, GLTextureCoords &coords) {
|
||||
void CropEffect::process_gl(double timecode, GLTextureCoords &coords) {
|
||||
// store initial coord data
|
||||
int left = coords.vertexTopLeftX;
|
||||
int top = coords.vertexTopLeftY;
|
||||
|
||||
@@ -7,7 +7,7 @@ class CropEffect : public Effect
|
||||
{
|
||||
public:
|
||||
CropEffect(Clip* c);
|
||||
void process_gl(double timecode, QOpenGLShaderProgram &shaders, GLTextureCoords &coords);
|
||||
void process_gl(double timecode, GLTextureCoords &coords);
|
||||
private:
|
||||
EffectField* left_field;
|
||||
EffectField* top_field;
|
||||
|
||||
@@ -10,7 +10,7 @@ FlipEffect::FlipEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_FLIP_EFFECT
|
||||
connect(vertical_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
}
|
||||
|
||||
void FlipEffect::process_gl(double timecode, QOpenGLShaderProgram &, GLTextureCoords &coords) {
|
||||
void FlipEffect::process_gl(double timecode, GLTextureCoords &coords) {
|
||||
if (horizontal_field->get_bool_value(timecode)) {
|
||||
double tlX = coords.textureTopLeftX;
|
||||
double blX = coords.textureBottomLeftX;
|
||||
|
||||
@@ -7,7 +7,7 @@ class FlipEffect : public Effect
|
||||
{
|
||||
public:
|
||||
FlipEffect(Clip* c);
|
||||
void process_gl(double timecode, QOpenGLShaderProgram &shaders, GLTextureCoords &coords);
|
||||
void process_gl(double timecode, GLTextureCoords &coords);
|
||||
private:
|
||||
EffectField* horizontal_field;
|
||||
EffectField* vertical_field;
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
#include "gaussianblureffect.h"
|
||||
|
||||
GaussianBlurEffect::GaussianBlurEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_GAUSSIANBLUR_EFFECT) {
|
||||
GaussianBlurEffect::GaussianBlurEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_GAUSSIANBLUR_EFFECT), frag(QOpenGLShader::Fragment), vert(QOpenGLShader::Vertex) {
|
||||
enable_opengl = true;
|
||||
|
||||
vert.compileSourceCode("varying vec2 vTexCoord;\nvoid main() {\n\tvTexCoord = gl_MultiTexCoord0.xy;\n\tgl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n}");
|
||||
frag.compileSourceCode("uniform mediump float amount_val;\nuniform 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)*amount_val), textureColor.g+((1.0-textureColor.g-textureColor.g)*amount_val), textureColor.b+((1.0-textureColor.b-textureColor.b)*amount_val), textureColor.a);\n}");
|
||||
program.addShader(&frag);
|
||||
program.addShader(&vert);
|
||||
program.link();
|
||||
}
|
||||
|
||||
void GaussianBlurEffect::process_gl(double timecode, QOpenGLShaderProgram &shaders, GLTextureCoords &coords) {
|
||||
shaders.addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec2 position;\nvoid main() {\ngl_Position = vec4(position, 1, 1);\n}");
|
||||
shaders.addShaderFromSourceCode(QOpenGLShader::Fragment, "uniform vec3 iResolution;\nuniform sampler2D iChannel0;\nuniform bool flip;\nuniform vec2 direction;\nvoid main() {\nvec2 uv = vec2(gl_FragCoord.xy / iResolution.xy);\nif (flip) uv.y = 1.0 - uv.y;\nvec4 color = vec4(0.0);\nvec2 off1 = vec2(1.3846153846) * direction;\nvec2 off2 = vec2(3.2307692308) * direction;\ncolor += texture2D(image, uv) * 0.2270270270;\ncolor += texture2D(image, uv + (off1 / resolution)) * 0.3162162162;\ncolor += texture2D(image, uv - (off1 / resolution)) * 0.3162162162;\ncolor += texture2D(image, uv + (off2 / resolution)) * 0.0702702703;\ncolor += texture2D(image, uv - (off2 / resolution)) * 0.0702702703;\ngl_FragColor = color;}");
|
||||
void GaussianBlurEffect::process_gl(double timecode, GLTextureCoords &coords) {
|
||||
program.bind();
|
||||
}
|
||||
|
||||
void GaussianBlurEffect::clean_gl() {
|
||||
program.release();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,12 @@ class GaussianBlurEffect : public Effect
|
||||
{
|
||||
public:
|
||||
GaussianBlurEffect(Clip* c);
|
||||
void process_gl(double timecode, QOpenGLShaderProgram &shaders, GLTextureCoords &coords);
|
||||
void process_gl(double timecode, GLTextureCoords &coords);
|
||||
void clean_gl();
|
||||
private:
|
||||
QOpenGLShaderProgram program;
|
||||
QOpenGLShader vert;
|
||||
QOpenGLShader frag;
|
||||
};
|
||||
|
||||
#endif // GAUSSIANBLUREFFECT_H
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
|
||||
#include <QLabel>
|
||||
#include <QGridLayout>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QXmlStreamAttributes>
|
||||
|
||||
InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_EFFECT) {
|
||||
InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_EFFECT), vert(QOpenGLShader::Vertex), frag(QOpenGLShader::Fragment) {
|
||||
enable_opengl = true;
|
||||
|
||||
EffectRow* amount_row = add_row("Amount:");
|
||||
@@ -20,10 +21,19 @@ InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_
|
||||
amount_val->set_double_default_value(100);
|
||||
|
||||
connect(amount_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
|
||||
vert.compileSourceCode("varying vec2 vTexCoord;\nvoid main() {\n\tvTexCoord = gl_MultiTexCoord0.xy;\n\tgl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n}");
|
||||
frag.compileSourceCode("uniform mediump float amount_val;\nuniform 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)*amount_val), textureColor.g+((1.0-textureColor.g-textureColor.g)*amount_val), textureColor.b+((1.0-textureColor.b-textureColor.b)*amount_val), textureColor.a);\n}");
|
||||
program.addShader(&vert);
|
||||
program.addShader(&frag);
|
||||
program.link();
|
||||
}
|
||||
|
||||
void InvertEffect::process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords&) {
|
||||
double value = amount_val->get_double_value(timecode)*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) + "), textureColor.a);\n}");
|
||||
void InvertEffect::process_gl(double timecode, GLTextureCoords&) {
|
||||
program.bind();
|
||||
program.setUniformValue("amount_val", (GLfloat) (amount_val->get_double_value(timecode)*0.01));
|
||||
}
|
||||
|
||||
void InvertEffect::clean_gl() {
|
||||
program.release();
|
||||
}
|
||||
|
||||
@@ -9,9 +9,13 @@ class InvertEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
InvertEffect(Clip* c);
|
||||
void process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords& coords);
|
||||
void process_gl(double timecode, GLTextureCoords& coords);
|
||||
void clean_gl();
|
||||
private:
|
||||
EffectField* amount_val;
|
||||
QOpenGLShader frag;
|
||||
QOpenGLShader vert;
|
||||
QOpenGLShaderProgram program;
|
||||
};
|
||||
|
||||
#endif // INVERTEFFECT_H
|
||||
|
||||
@@ -49,7 +49,7 @@ void ShakeEffect::refresh() {
|
||||
}
|
||||
}
|
||||
|
||||
void ShakeEffect::process_gl(double timecode, QOpenGLShaderProgram&, GLTextureCoords&) {
|
||||
void ShakeEffect::process_gl(double timecode, GLTextureCoords&) {
|
||||
if (shake_progress > shake_limit) {
|
||||
double ival = intensity_val->get_double_value(timecode);
|
||||
if ((int)ival > 0) {
|
||||
|
||||
@@ -7,7 +7,7 @@ class ShakeEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShakeEffect(Clip* c);
|
||||
void process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords& coords);
|
||||
void process_gl(double timecode, GLTextureCoords& coords);
|
||||
|
||||
EffectField* intensity_val;
|
||||
EffectField* rotation_val;
|
||||
|
||||
@@ -40,7 +40,7 @@ void SolidEffect::enable_color() {
|
||||
solid_color_field->set_enabled(solid_type->get_combo_data(-1) == SOLID_TYPE_COLOR);
|
||||
}
|
||||
|
||||
void SolidEffect::process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords& coords) {
|
||||
void SolidEffect::process_gl(double timecode, GLTextureCoords& coords) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ public:
|
||||
EffectField* solid_type;
|
||||
EffectField* solid_color_field;
|
||||
EffectField* opacity_field;
|
||||
void process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords& coords);
|
||||
void process_gl(double timecode, GLTextureCoords& coords);
|
||||
void process_image(long p, uint8_t* data, int width, int height);
|
||||
private slots:
|
||||
void enable_color();
|
||||
|
||||
@@ -121,7 +121,7 @@ void TransformEffect::toggle_uniform_scale(bool enabled) {
|
||||
scale_y->set_enabled(!enabled);
|
||||
}
|
||||
|
||||
void TransformEffect::process_gl(double timecode, QOpenGLShaderProgram&, GLTextureCoords& coords) {
|
||||
void TransformEffect::process_gl(double timecode, GLTextureCoords& coords) {
|
||||
// position
|
||||
glTranslatef(position_x->get_double_value(timecode)-(parent_clip->sequence->width/2), position_y->get_double_value(timecode)-(parent_clip->sequence->height/2), 0);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ class TransformEffect : public Effect {
|
||||
public:
|
||||
TransformEffect(Clip* c);
|
||||
void refresh();
|
||||
void process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords& coords);
|
||||
void process_gl(double timecode, GLTextureCoords& coords);
|
||||
|
||||
EffectField* position_x;
|
||||
EffectField* position_y;
|
||||
|
||||
+9
-4
@@ -844,15 +844,19 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
stream.readNext();
|
||||
if (stream.name() == "effect" && stream.isStartElement()) {
|
||||
int effect_id = -1;
|
||||
bool effect_enabled = true;
|
||||
for (int j=0;j<stream.attributes().size();j++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(j);
|
||||
if (attr.name().toString() == QLatin1String("id")) {
|
||||
effect_id = attr.value().toInt();
|
||||
break;
|
||||
}
|
||||
if (attr.name() == "id") {
|
||||
effect_id = attr.value().toInt();
|
||||
} else if (attr.name() == "enabled") {
|
||||
effect_enabled = (attr.value() == "1");
|
||||
}
|
||||
}
|
||||
if (effect_id != -1) {
|
||||
Effect* e = create_effect(effect_id, c);
|
||||
e->set_enabled(effect_enabled);
|
||||
// stream.writeAttribute("enabled", QString::number(e->is_enabled()));
|
||||
e->load(stream);
|
||||
c->effects.append(e);
|
||||
}
|
||||
@@ -1111,6 +1115,7 @@ void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int
|
||||
stream.writeStartElement("effect"); // effect
|
||||
Effect* e = c->effects.at(k);
|
||||
stream.writeAttribute("id", QString::number(e->id));
|
||||
stream.writeAttribute("enabled", QString::number(e->is_enabled()));
|
||||
e->save(stream);
|
||||
stream.writeEndElement(); // effect
|
||||
}
|
||||
|
||||
+11
-9
@@ -304,16 +304,18 @@ void Timeline::redraw_all_clips(bool changed) {
|
||||
}
|
||||
|
||||
void Timeline::select_all() {
|
||||
selections.clear();
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
Clip* c = sequence->get_clip(i);
|
||||
Selection s;
|
||||
s.in = c->timeline_in;
|
||||
s.out = c->timeline_out;
|
||||
s.track = c->track;
|
||||
if (c != NULL) selections.append(s);
|
||||
if (sequence != NULL) {
|
||||
selections.clear();
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
Clip* c = sequence->get_clip(i);
|
||||
Selection s;
|
||||
s.in = c->timeline_in;
|
||||
s.out = c->timeline_out;
|
||||
s.track = c->track;
|
||||
if (c != NULL) selections.append(s);
|
||||
}
|
||||
repaint_timeline();
|
||||
}
|
||||
repaint_timeline();
|
||||
}
|
||||
|
||||
void Timeline::delete_in_out(bool ripple) {
|
||||
|
||||
+87
-90
@@ -80,108 +80,105 @@ bool get_clip_frame(Clip* c, long playhead) {
|
||||
long clip_time = refactor_frame_number(sequence_clip_time, c->sequence->frame_rate, av_q2d(av_guess_frame_rate(c->formatCtx, c->stream, c->frame)));
|
||||
|
||||
// do we need to update the texture?
|
||||
MediaStream* ms = static_cast<Media*>(c->media)->get_stream_from_file_index(c->media_stream);
|
||||
if ((!ms->infinite_length/* && c->texture_frame != clip_time*/) ||
|
||||
(ms->infinite_length && c->texture_frame == -1)) {
|
||||
AVFrame* current_frame = NULL;
|
||||
bool no_frame = false;
|
||||
MediaStream* ms = static_cast<Media*>(c->media)->get_stream_from_file_index(c->media_stream);
|
||||
|
||||
// get frame data
|
||||
if (ms->infinite_length) { // if clip is a still frame, we only need one
|
||||
if (c->cache_A.written) {
|
||||
// retrieve cached frame
|
||||
current_frame = c->cache_A.frames[0];
|
||||
} else if (c->lock.tryLock()) {
|
||||
// grab image
|
||||
cache_clip(c, 0, true, false, false, NULL);
|
||||
c->lock.unlock();
|
||||
}
|
||||
} else {
|
||||
// keeping a RAM cache improves performance, however it's detrimental when rendering
|
||||
// determine which cache contains the requested frame
|
||||
bool using_cache_A = false;
|
||||
bool using_cache_B = false;
|
||||
AVFrame** cache = NULL;
|
||||
long cache_offset = 0;
|
||||
bool cache_needs_reset = false;
|
||||
AVFrame* current_frame = NULL;
|
||||
bool no_frame = false;
|
||||
|
||||
if (c->cache_A.written && clip_time >= c->cache_A.offset && clip_time < c->cache_A.offset + c->cache_size) {
|
||||
if (clip_time < c->cache_A.offset + c->cache_A.write_count) {
|
||||
if (c->cache_A.mutex.tryLock()) { // lock in case cacher is still writing to it
|
||||
using_cache_A = true;
|
||||
c->cache_A.unread = false;
|
||||
cache = c->cache_A.frames;
|
||||
cache_offset = c->cache_A.offset;
|
||||
c->cache_A.mutex.unlock();
|
||||
}
|
||||
} else {
|
||||
no_frame = true;
|
||||
}
|
||||
} else if (c->cache_B.written && clip_time >= c->cache_B.offset && clip_time < c->cache_B.offset + c->cache_size) {
|
||||
if (clip_time < c->cache_B.offset + c->cache_B.write_count) {
|
||||
if (c->cache_B.mutex.tryLock()) { // lock in case cacher is still writing to it
|
||||
using_cache_B = true;
|
||||
c->cache_B.unread = false;
|
||||
cache = c->cache_B.frames;
|
||||
cache_offset = c->cache_B.offset;
|
||||
c->cache_B.mutex.unlock();
|
||||
}
|
||||
} else {
|
||||
no_frame = true;
|
||||
// get frame data
|
||||
if (ms->infinite_length) { // if clip is a still frame, we only need one
|
||||
if (c->cache_A.written) {
|
||||
// retrieve cached frame
|
||||
current_frame = c->cache_A.frames[0];
|
||||
} else if (c->lock.tryLock()) {
|
||||
// grab image
|
||||
cache_clip(c, 0, true, false, false, NULL);
|
||||
c->lock.unlock();
|
||||
}
|
||||
} else {
|
||||
// keeping a RAM cache improves performance, however it's detrimental when rendering
|
||||
// determine which cache contains the requested frame
|
||||
bool using_cache_A = false;
|
||||
bool using_cache_B = false;
|
||||
AVFrame** cache = NULL;
|
||||
long cache_offset = 0;
|
||||
bool cache_needs_reset = false;
|
||||
|
||||
if (c->cache_A.written && clip_time >= c->cache_A.offset && clip_time < c->cache_A.offset + c->cache_size) {
|
||||
if (clip_time < c->cache_A.offset + c->cache_A.write_count) {
|
||||
if (c->cache_A.mutex.tryLock()) { // lock in case cacher is still writing to it
|
||||
using_cache_A = true;
|
||||
c->cache_A.unread = false;
|
||||
cache = c->cache_A.frames;
|
||||
cache_offset = c->cache_A.offset;
|
||||
c->cache_A.mutex.unlock();
|
||||
}
|
||||
} else {
|
||||
// this is technically bad, unless we just seeked
|
||||
c->cache_A.unread = c->cache_B.unread = false;
|
||||
cache_needs_reset = true;
|
||||
no_frame = true;
|
||||
}
|
||||
} else if (c->cache_B.written && clip_time >= c->cache_B.offset && clip_time < c->cache_B.offset + c->cache_size) {
|
||||
if (clip_time < c->cache_B.offset + c->cache_B.write_count) {
|
||||
if (c->cache_B.mutex.tryLock()) { // lock in case cacher is still writing to it
|
||||
using_cache_B = true;
|
||||
c->cache_B.unread = false;
|
||||
cache = c->cache_B.frames;
|
||||
cache_offset = c->cache_B.offset;
|
||||
c->cache_B.mutex.unlock();
|
||||
}
|
||||
} else {
|
||||
no_frame = true;
|
||||
}
|
||||
} else {
|
||||
// this is technically bad, unless we just seeked
|
||||
c->cache_A.unread = c->cache_B.unread = false;
|
||||
cache_needs_reset = true;
|
||||
}
|
||||
|
||||
if (!no_frame) {
|
||||
if (cache != NULL) {
|
||||
current_frame = cache[clip_time - cache_offset];
|
||||
}
|
||||
|
||||
if (!no_frame) {
|
||||
if (cache != NULL) {
|
||||
current_frame = cache[clip_time - cache_offset];
|
||||
}
|
||||
|
||||
// determine whether we should start filling the other cache
|
||||
if (!using_cache_A || !using_cache_B) {
|
||||
if (c->lock.tryLock()) {
|
||||
bool write_A = (!using_cache_A && !c->cache_A.unread);
|
||||
bool write_B = (!using_cache_B && !c->cache_B.unread);
|
||||
if (write_A || write_B) {
|
||||
// if we have no cache and need to seek, start us at the current playhead, otherwise start at the end of the current cache
|
||||
cache_clip(c, (cache_needs_reset) ? clip_time : cache_offset + c->cache_size, write_A, write_B, cache_needs_reset, NULL);
|
||||
}
|
||||
c->lock.unlock();
|
||||
// determine whether we should start filling the other cache
|
||||
if (!using_cache_A || !using_cache_B) {
|
||||
if (c->lock.tryLock()) {
|
||||
bool write_A = (!using_cache_A && !c->cache_A.unread);
|
||||
bool write_B = (!using_cache_B && !c->cache_B.unread);
|
||||
if (write_A || write_B) {
|
||||
// if we have no cache and need to seek, start us at the current playhead, otherwise start at the end of the current cache
|
||||
cache_clip(c, (cache_needs_reset) ? clip_time : cache_offset + c->cache_size, write_A, write_B, cache_needs_reset, NULL);
|
||||
}
|
||||
c->lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current_frame != NULL) {
|
||||
// set up opengl texture
|
||||
if (c->texture == NULL) {
|
||||
c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
c->texture->setSize(current_frame->width, current_frame->height);
|
||||
c->texture->setFormat(QOpenGLTexture::RGBA8_UNorm);
|
||||
c->texture->setMipLevels(c->texture->maximumMipLevels());
|
||||
c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
|
||||
c->texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8);
|
||||
}
|
||||
|
||||
|
||||
memcpy(c->comp_frame, current_frame->data[0], c->comp_frame_size);
|
||||
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, c->comp_frame, current_frame->width, current_frame->height);
|
||||
}
|
||||
}
|
||||
|
||||
c->texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, c->comp_frame);
|
||||
c->texture_frame = clip_time;
|
||||
|
||||
return true;
|
||||
} else if (!no_frame) {
|
||||
texture_failed = true;
|
||||
qDebug() << "[ERROR] Failed to retrieve frame from cache (R:" << clip_time << "| A:" << c->cache_A.offset << "-" << c->cache_A.offset+c->cache_size-1 << "| B:" << c->cache_B.offset << "-" << c->cache_B.offset+c->cache_size-1 << "| WA:" << c->cache_A.written << "| WB:" << c->cache_B.written << ")";
|
||||
if (current_frame != NULL) {
|
||||
// set up opengl texture
|
||||
if (c->texture == NULL) {
|
||||
c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
c->texture->setSize(current_frame->width, current_frame->height);
|
||||
c->texture->setFormat(QOpenGLTexture::RGBA8_UNorm);
|
||||
c->texture->setMipLevels(c->texture->maximumMipLevels());
|
||||
c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
|
||||
c->texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8);
|
||||
}
|
||||
|
||||
memcpy(c->comp_frame, current_frame->data[0], c->comp_frame_size);
|
||||
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, c->comp_frame, current_frame->width, current_frame->height);
|
||||
}
|
||||
}
|
||||
|
||||
c->texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, c->comp_frame);
|
||||
c->texture_frame = clip_time;
|
||||
|
||||
return true;
|
||||
} else if (!no_frame) {
|
||||
texture_failed = true;
|
||||
qDebug() << "[ERROR] Failed to retrieve frame from cache (R:" << clip_time << "| A:" << c->cache_A.offset << "-" << c->cache_A.offset+c->cache_size-1 << "| B:" << c->cache_B.offset << "-" << c->cache_B.offset+c->cache_size-1 << "| WA:" << c->cache_A.written << "| WB:" << c->cache_B.written << ")";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
+7
-8
@@ -180,11 +180,9 @@ void ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio) {
|
||||
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(((double)(panel_timeline->playhead-c->timeline_in+c->clip_in)/(double)sequence->frame_rate), shader, coords);
|
||||
c->effects.at(j)->process_gl(((double)(panel_timeline->playhead-c->timeline_in+c->clip_in)/(double)sequence->frame_rate), coords);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,9 +200,6 @@ void ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio) {
|
||||
}
|
||||
}
|
||||
|
||||
bool use_gl_shaders = shader.link();
|
||||
if (use_gl_shaders) shader.bind();
|
||||
|
||||
c->texture->bind();
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
@@ -218,9 +213,13 @@ void ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio) {
|
||||
glVertex2f(coords.vertexBottomLeftX, coords.vertexBottomLeftY); // bottom left
|
||||
glEnd();
|
||||
|
||||
c->texture->release();
|
||||
c->texture->release();
|
||||
|
||||
if (use_gl_shaders) shader.release();
|
||||
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)->clean_gl();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (render_audio &&
|
||||
c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
|
||||
|
||||
Reference in New Issue
Block a user