diff --git a/effects/audio/paneffect.cpp b/effects/audio/paneffect.cpp index c8065cdce..c9c012910 100644 --- a/effects/audio/paneffect.cpp +++ b/effects/audio/paneffect.cpp @@ -20,26 +20,25 @@ PanEffect::PanEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_PAN_EFFECT) { connect(pan_val, SIGNAL(changed()), this, SLOT(field_changed())); } -void PanEffect::process_audio(quint8 *samples, int nb_bytes) { - double pval = pan_val->get_double_value(-1); - if (pval != 0) { - for (int i=0;iget_double_value(timecode_start+(interval*i)); + qint16 left_sample = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF)); + qint16 right_sample = (qint16) (((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF)); - double val = qPow(pval*0.01, 3); - if (val < 0) { - // affect right channel - right_sample *= (1-std::abs(val)); - } else { - // affect left channel - left_sample *= (1-val); - } + double val = qPow(pval*0.01, 3); + if (val < 0) { + // affect right channel + right_sample *= (1-std::abs(val)); + } else { + // affect left channel + left_sample *= (1-val); + } - samples[i+3] = (quint8) (right_sample >> 8); - samples[i+2] = (quint8) right_sample; - samples[i+1] = (quint8) (left_sample >> 8); - samples[i] = (quint8) left_sample; - } - } + samples[i+3] = (quint8) (right_sample >> 8); + samples[i+2] = (quint8) right_sample; + samples[i+1] = (quint8) (left_sample >> 8); + samples[i] = (quint8) left_sample; + } } diff --git a/effects/audio/paneffect.h b/effects/audio/paneffect.h index 701239ba1..d33723b62 100644 --- a/effects/audio/paneffect.h +++ b/effects/audio/paneffect.h @@ -6,7 +6,7 @@ class PanEffect : public Effect { public: PanEffect(Clip* c); - void process_audio(quint8* samples, int nb_bytes); + void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); EffectField* pan_val; }; diff --git a/effects/audio/volumeeffect.cpp b/effects/audio/volumeeffect.cpp index d089ca62e..10fe1a2d3 100644 --- a/effects/audio/volumeeffect.cpp +++ b/effects/audio/volumeeffect.cpp @@ -21,21 +21,22 @@ VolumeEffect::VolumeEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_VOLUME_ connect(volume_val, SIGNAL(changed()), this, SLOT(field_changed())); } -void VolumeEffect::process_audio(quint8* samples, int nb_bytes) { - double vol_val = volume_val->get_double_value(-1); - if (vol_val != 100) { - for (int i=0;i INT16_MAX) { - samp = INT16_MAX; - } else if (samp < INT16_MIN) { - samp = INT16_MIN; - } +void VolumeEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) { +// qDebug() << timecode_start << timecode_end; + double interval = (timecode_end-timecode_start)/nb_bytes; + for (int i=0;iget_double_value(timecode_start+(interval*i)); +// qDebug() << timecode_start+(interval*i); + qint32 samp = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF)); + double val = qPow(vol_val*0.01, 3); + samp *= val; + if (samp > INT16_MAX) { + samp = INT16_MAX; + } else if (samp < INT16_MIN) { + samp = INT16_MIN; + } - samples[i+1] = (quint8) (samp >> 8); - samples[i] = (quint8) samp; - } - } + samples[i+1] = (quint8) (samp >> 8); + samples[i] = (quint8) samp; + } } diff --git a/effects/audio/volumeeffect.h b/effects/audio/volumeeffect.h index 9599d1ffe..60924d597 100644 --- a/effects/audio/volumeeffect.h +++ b/effects/audio/volumeeffect.h @@ -6,7 +6,7 @@ class VolumeEffect : public Effect { public: VolumeEffect(Clip* c); - void process_audio(quint8* samples, int nb_bytes); + void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); EffectField* volume_val; }; diff --git a/effects/effect.cpp b/effects/effect.cpp index eb9e397d1..0dbb702c4 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -6,6 +6,7 @@ #include "ui/collapsiblewidget.h" #include "panels/project.h" #include "project/undo.h" +#include "project/sequence.h" #include "ui/labelslider.h" #include "ui/colorbutton.h" #include "ui/comboboxex.h" @@ -21,6 +22,7 @@ #include "effects/video/shakeeffect.h" #include "effects/video/solideffect.h" #include "effects/video/texteffect.h" +#include "effects/video/chromakeyeffect.h" #include "effects/audio/paneffect.h" #include "effects/audio/volumeeffect.h" @@ -43,6 +45,7 @@ void init_effects() { video_effect_names[VIDEO_TEXT_EFFECT] = "Text"; video_effect_names[VIDEO_SOLID_EFFECT] = "Solid"; video_effect_names[VIDEO_INVERT_EFFECT] = "Invert"; + video_effect_names[VIDEO_CHROMAKEY_EFFECT] = "Chroma Key"; audio_effect_names[AUDIO_VOLUME_EFFECT] = "Volume"; audio_effect_names[AUDIO_PAN_EFFECT] = "Pan"; @@ -56,6 +59,7 @@ Effect* create_effect(int effect_id, Clip* c) { case VIDEO_TEXT_EFFECT: return new TextEffect(c); break; case VIDEO_SOLID_EFFECT: return new SolidEffect(c); break; case VIDEO_INVERT_EFFECT: return new InvertEffect(c); break; + case VIDEO_CHROMAKEY_EFFECT: return new ChromaKeyEffect(c); break; } } else { switch (effect_id) { @@ -276,9 +280,9 @@ Effect* Effect::copy(Clip* c) { return copy; } -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) {} +void Effect::process_image(double, uint8_t*, int, int) {} +void Effect::process_gl(double, QOpenGLShaderProgram&, GLTextureCoords&) {} +void Effect::process_audio(double, double, quint8*, int, int) {} /* Effect Row Definitions */ @@ -447,7 +451,15 @@ QVariant EffectField::get_current_data() { case EFFECT_FIELD_COMBO: return static_cast(ui_element)->currentIndex(); break; case EFFECT_FIELD_FONT: return static_cast(ui_element)->currentText(); break; } - return QVariant(); + return QVariant(); +} + +double EffectField::frameToTimecode(long frame) { + return ((double) frame / parent_row->parent_effect->parent_clip->sequence->frame_rate); +} + +long EffectField::timecodeToFrame(double timecode) { + return qRound(timecode * parent_row->parent_effect->parent_clip->sequence->frame_rate); } void EffectField::set_current_data(const QVariant& data) { @@ -461,59 +473,51 @@ void EffectField::set_current_data(const QVariant& data) { } } -void EffectField::get_keyframe_data(long frame, int &before, int &after, double &progress) { +void EffectField::get_keyframe_data(double timecode, int &before, int &after, double &progress) { int before_keyframe_index = -1; int after_keyframe_index = -1; long before_keyframe_time = LONG_MIN; long after_keyframe_time = LONG_MAX; + long frame = timecodeToFrame(timecode); for (int i=0;ikeyframe_times.size();i++) { long eval_keyframe_time = parent_row->keyframe_times.at(i); - if (eval_keyframe_time == frame) { + if (eval_keyframe_time == frame) { before = i; after = i; return; - } else if (eval_keyframe_time < frame && eval_keyframe_time > before_keyframe_time) { + } else if (eval_keyframe_time < frame && eval_keyframe_time > before_keyframe_time) { before_keyframe_index = i; before_keyframe_time = eval_keyframe_time; - } else if (eval_keyframe_time > frame && eval_keyframe_time < after_keyframe_time) { + } else if (eval_keyframe_time > frame && eval_keyframe_time < after_keyframe_time) { after_keyframe_index = i; after_keyframe_time = eval_keyframe_time; } } - if (type == EFFECT_FIELD_DOUBLE || type == EFFECT_FIELD_COLOR) { - if (before_keyframe_index > -1 && after_keyframe_index > -1) { - // interpolate - before = before_keyframe_index; - after = after_keyframe_index; - progress = (double)(frame-before_keyframe_time)/(double)(after_keyframe_time-before_keyframe_time); + if ((type == EFFECT_FIELD_DOUBLE || type == EFFECT_FIELD_COLOR) && (before_keyframe_index > -1 && after_keyframe_index > -1)) { + // interpolate + before = before_keyframe_index; + after = after_keyframe_index; - // TODO routines for bezier - currently this is purely linear - } else if (before_keyframe_index > -1) { - before = before_keyframe_index; - after = before_keyframe_index; - } else if (after_keyframe_index > -1) { - before = after_keyframe_index; - after = after_keyframe_index; - } + progress = (timecode-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(before_keyframe_time)); + + // TODO routines for bezier - currently this is purely linear + } else if (before_keyframe_index > -1) { + 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_index; - after = after_keyframe_index; - } - } + before = after_keyframe_index; + after = after_keyframe_index; + } } -void EffectField::validate_keyframe_data(long frame) { +void EffectField::validate_keyframe_data(double timecode) { if (parent_row->isKeyframing()) { int before_keyframe; int after_keyframe; double progress; - get_keyframe_data(frame, before_keyframe, after_keyframe, progress); + get_keyframe_data(timecode, before_keyframe, after_keyframe, progress); const QVariant& before_data = keyframe_data.at(before_keyframe); switch (type) { @@ -574,8 +578,8 @@ void EffectField::set_enabled(bool e) { ui_element->setEnabled(e); } -double EffectField::get_double_value(long p) { - validate_keyframe_data(p); +double EffectField::get_double_value(double timecode) { + validate_keyframe_data(timecode); return static_cast(ui_element)->value(); } @@ -599,18 +603,18 @@ void EffectField::add_combo_item(const QString& name, const QVariant& data) { static_cast(ui_element)->addItem(name, data); } -int EffectField::get_combo_index(long p) { - validate_keyframe_data(p); +int EffectField::get_combo_index(double timecode) { + validate_keyframe_data(timecode); return static_cast(ui_element)->currentIndex(); } -const QVariant EffectField::get_combo_data(long p) { - validate_keyframe_data(p); +const QVariant EffectField::get_combo_data(double timecode) { + validate_keyframe_data(timecode); return static_cast(ui_element)->currentData(); } -const QString EffectField::get_combo_string(long p) { - validate_keyframe_data(p); +const QString EffectField::get_combo_string(double timecode) { + validate_keyframe_data(timecode); return static_cast(ui_element)->currentText(); } @@ -622,8 +626,8 @@ void EffectField::set_combo_string(const QString& s) { static_cast(ui_element)->setCurrentTextEx(s); } -bool EffectField::get_bool_value(long p) { - validate_keyframe_data(p); +bool EffectField::get_bool_value(double timecode) { + validate_keyframe_data(timecode); return static_cast(ui_element)->isChecked(); } @@ -631,8 +635,8 @@ void EffectField::set_bool_value(bool b) { return static_cast(ui_element)->setChecked(b); } -const QString EffectField::get_string_value(long p) { - validate_keyframe_data(p); +const QString EffectField::get_string_value(double timecode) { + validate_keyframe_data(timecode); return static_cast(ui_element)->toPlainText(); } @@ -640,8 +644,8 @@ void EffectField::set_string_value(const QString& s) { static_cast(ui_element)->setText(s); } -const QString EffectField::get_font_name(long p) { - validate_keyframe_data(p); +const QString EffectField::get_font_name(double timecode) { + validate_keyframe_data(timecode); return static_cast(ui_element)->currentText(); } @@ -649,8 +653,8 @@ void EffectField::set_font_name(const QString& s) { static_cast(ui_element)->setCurrentText(s); } -QColor EffectField::get_color_value(long p) { - validate_keyframe_data(p); +QColor EffectField::get_color_value(double timecode) { + validate_keyframe_data(timecode); return static_cast(ui_element)->get_color(); } diff --git a/effects/effect.h b/effects/effect.h index 60b9adcda..6e19f7b6c 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -25,6 +25,7 @@ enum VideoEffects { VIDEO_TEXT_EFFECT, VIDEO_SOLID_EFFECT, VIDEO_INVERT_EFFECT, + VIDEO_CHROMAKEY_EFFECT, VIDEO_EFFECT_COUNT }; @@ -82,35 +83,37 @@ public: int type; QVariant get_current_data(); + double frameToTimecode(long frame); + long timecodeToFrame(double timecode); void set_current_data(const QVariant&); - void get_keyframe_data(long frame, int& before, int& after, double& d); - void validate_keyframe_data(long frame); + void get_keyframe_data(double timecode, int& before, int& after, double& d); + void validate_keyframe_data(double timecode); // QVariant get_keyframe_data(long p); // bool is_keyframed(long p); - double get_double_value(long p); + double get_double_value(double timecode); void set_double_value(double v); void set_double_default_value(double v); void set_double_minimum_value(double v); void set_double_maximum_value(double v); - const QString get_string_value(long p); + const QString get_string_value(double timecode); void set_string_value(const QString &s); void add_combo_item(const QString& name, const QVariant &data); - int get_combo_index(long p); - const QVariant get_combo_data(long p); - const QString get_combo_string(long p); + int get_combo_index(double timecode); + const QVariant get_combo_data(double timecode); + const QString get_combo_string(double timecode); void set_combo_index(int index); void set_combo_string(const QString& s); - bool get_bool_value(long p); + bool get_bool_value(double timecode); void set_bool_value(bool b); - const QString get_font_name(long p); + const QString get_font_name(double timecode); void set_font_name(const QString& s); - QColor get_color_value(long p); + QColor get_color_value(double timecode); void set_color_value(QColor color); QWidget* get_ui_element(); @@ -188,9 +191,9 @@ public: const char* ffmpeg_filter; - 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); + 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_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); public slots: void field_changed(); private: diff --git a/effects/video/chromakeyeffect.cpp b/effects/video/chromakeyeffect.cpp new file mode 100644 index 000000000..db43e98e9 --- /dev/null +++ b/effects/video/chromakeyeffect.cpp @@ -0,0 +1,15 @@ +#include "chromakeyeffect.h" + +ChromaKeyEffect::ChromaKeyEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_CHROMAKEY_EFFECT) { + 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); +} + +void ChromaKeyEffect::process_gl(double timecode, QOpenGLShaderProgram &shaders, GLTextureCoords&) { + +} diff --git a/effects/video/chromakeyeffect.h b/effects/video/chromakeyeffect.h new file mode 100644 index 000000000..5569ff9c0 --- /dev/null +++ b/effects/video/chromakeyeffect.h @@ -0,0 +1,15 @@ +#ifndef CHROMAKEYEFFECT_H +#define CHROMAKEYEFFECT_H + +#include "effects/effect.h" + +class ChromaKeyEffect : public Effect { +public: + ChromaKeyEffect(Clip* c); + void process_gl(double timecode, QOpenGLShaderProgram &shaders, GLTextureCoords &coords); +private: + EffectField* color_field; + EffectField* tolerance_field; +}; + +#endif // CHROMAKEYEFFECT_H diff --git a/effects/video/inverteffect.cpp b/effects/video/inverteffect.cpp index 9728800cd..23c67e6ed 100644 --- a/effects/video/inverteffect.cpp +++ b/effects/video/inverteffect.cpp @@ -22,8 +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 frame, QOpenGLShaderProgram& shaders, GLTextureCoords&) { - double value = amount_val->get_double_value(frame)*0.01; +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) + "), 1);\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}"); } diff --git a/effects/video/inverteffect.h b/effects/video/inverteffect.h index 4007ed5f2..18a2b939c 100644 --- a/effects/video/inverteffect.h +++ b/effects/video/inverteffect.h @@ -9,7 +9,7 @@ class InvertEffect : public Effect { Q_OBJECT public: InvertEffect(Clip* c); - void process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords); + void process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords& coords); private: EffectField* amount_val; }; diff --git a/effects/video/shakeeffect.cpp b/effects/video/shakeeffect.cpp index 73a40b649..a11f71ab7 100644 --- a/effects/video/shakeeffect.cpp +++ b/effects/video/shakeeffect.cpp @@ -49,9 +49,9 @@ void ShakeEffect::refresh() { } } -void ShakeEffect::process_gl(long frame, QOpenGLShaderProgram&, GLTextureCoords&) { +void ShakeEffect::process_gl(double timecode, QOpenGLShaderProgram&, GLTextureCoords&) { if (shake_progress > shake_limit) { - double ival = intensity_val->get_double_value(frame); + double ival = intensity_val->get_double_value(timecode); if ((int)ival > 0) { prev_x = next_x; prev_y = next_y; @@ -81,7 +81,7 @@ void ShakeEffect::process_gl(long frame, QOpenGLShaderProgram&, GLTextureCoords& offset_x = 0; offset_y = 0; } - double rot_val = rotation_val->get_double_value(frame); + double rot_val = rotation_val->get_double_value(timecode); if ((int)rot_val > 0) { prev_rot = next_rot; next_rot = (qrand() % (int) (rot_val * 2)) - rot_val; diff --git a/effects/video/shakeeffect.h b/effects/video/shakeeffect.h index 209163962..69f80fec3 100644 --- a/effects/video/shakeeffect.h +++ b/effects/video/shakeeffect.h @@ -7,7 +7,7 @@ class ShakeEffect : public Effect { Q_OBJECT public: ShakeEffect(Clip* c); - void process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords); + void process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords& coords); EffectField* intensity_val; EffectField* rotation_val; diff --git a/effects/video/solideffect.cpp b/effects/video/solideffect.cpp index 94c2444e6..26ea42af0 100644 --- a/effects/video/solideffect.cpp +++ b/effects/video/solideffect.cpp @@ -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(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords) { +void SolidEffect::process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords& coords) { } diff --git a/effects/video/solideffect.h b/effects/video/solideffect.h index de065835b..0d238115d 100644 --- a/effects/video/solideffect.h +++ b/effects/video/solideffect.h @@ -10,7 +10,7 @@ public: EffectField* solid_type; EffectField* solid_color_field; EffectField* opacity_field; - void process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords); + void process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords& coords); void process_image(long p, uint8_t* data, int width, int height); private slots: void enable_color(); diff --git a/effects/video/texteffect.cpp b/effects/video/texteffect.cpp index 873ce16af..492306f8c 100644 --- a/effects/video/texteffect.cpp +++ b/effects/video/texteffect.cpp @@ -242,7 +242,7 @@ void blurred2(QImage& result, const QRect& rect, int radius, bool alphaOnly = fa } } -void TextEffect::process_image(long frame, uint8_t* data, int w, int h) { +void TextEffect::process_image(double timecode, uint8_t* data, int w, int h) { QImage img(data, w, h, QImage::Format_RGBA8888); QPainter p(&img); p.setRenderHint(QPainter::Antialiasing); @@ -251,15 +251,15 @@ void TextEffect::process_image(long frame, uint8_t* data, int w, int h) { // set font font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias); - font.setFamily(set_font_combobox->get_font_name(frame)); - font.setPointSize(size_val->get_double_value(frame)); + font.setFamily(set_font_combobox->get_font_name(timecode)); + font.setPointSize(size_val->get_double_value(timecode)); p.setFont(font); QFontMetrics fm(font); - QStringList lines = text_val->get_string_value(frame).split('\n'); + QStringList lines = text_val->get_string_value(timecode).split('\n'); // word wrap function - if (word_wrap_field->get_bool_value(frame)) { + if (word_wrap_field->get_bool_value(timecode)) { for (int i=0;i width) { @@ -288,7 +288,7 @@ void TextEffect::process_image(long frame, uint8_t* data, int w, int h) { for (int i=0;iget_combo_data(frame).toInt()) { + switch (halign_field->get_combo_data(timecode).toInt()) { case Qt::AlignLeft: text_x = 0; break; case Qt::AlignHCenter: text_x = (width/2) - (fm.width(lines.at(i))/2); break; case Qt::AlignRight: text_x = width - fm.width(lines.at(i)); break; @@ -317,7 +317,7 @@ void TextEffect::process_image(long frame, uint8_t* data, int w, int h) { break; } - switch (valign_field->get_combo_data(frame).toInt()) { + switch (valign_field->get_combo_data(timecode).toInt()) { case Qt::AlignTop: text_y = (fm.height()*i)+fm.ascent(); break; case Qt::AlignVCenter: text_y = ((height/2) - (text_height/2) - fm.descent()) + (fm.height()*(i+1)); break; case Qt::AlignBottom: text_y = (height - text_height - fm.descent()) + (fm.height()*(i+1)); break; @@ -329,32 +329,32 @@ void TextEffect::process_image(long frame, uint8_t* data, int w, int h) { p.setPen(Qt::NoPen); // draw shadow - if (shadow_bool->get_bool_value(frame)) { + if (shadow_bool->get_bool_value(timecode)) { QImage shadow(width, height, QImage::Format_ARGB32_Premultiplied); shadow.fill(Qt::transparent); QPainter spaint(&shadow); - int shadow_offset = shadow_distance->get_double_value(frame); - QColor col = shadow_color->get_color_value(frame); - col.setAlphaF(shadow_opacity->get_double_value(frame)*0.01); + int shadow_offset = shadow_distance->get_double_value(timecode); + QColor col = shadow_color->get_color_value(timecode); + col.setAlphaF(shadow_opacity->get_double_value(timecode)*0.01); spaint.setBrush(col); spaint.drawPath(path); - blurred2(shadow, shadow.rect(), shadow_softness->get_double_value(frame), false); + blurred2(shadow, shadow.rect(), shadow_softness->get_double_value(timecode), false); p.drawImage(shadow_offset, shadow_offset, shadow); spaint.end(); } // draw outline - int outline_width_val = outline_width->get_double_value(frame); - if (outline_bool->get_bool_value(frame) && outline_width_val > 0) { - QPen outline(outline_color->get_color_value(frame)); + int outline_width_val = outline_width->get_double_value(timecode); + if (outline_bool->get_bool_value(timecode) && outline_width_val > 0) { + QPen outline(outline_color->get_color_value(timecode)); outline.setWidth(outline_width_val); p.setPen(outline); } // draw "master" text - p.setBrush(set_color_button->get_color_value(frame)); + p.setBrush(set_color_button->get_color_value(timecode)); p.drawPath(path); } diff --git a/effects/video/texteffect.h b/effects/video/texteffect.h index ca53fee71..0a0587f6d 100644 --- a/effects/video/texteffect.h +++ b/effects/video/texteffect.h @@ -9,7 +9,7 @@ class TextEffect : public Effect { Q_OBJECT public: TextEffect(Clip* c); - void process_image(long frame, uint8_t* data, int width, int height); + void process_image(double timecode, uint8_t* data, int width, int height); EffectField* text_val; EffectField* size_val; diff --git a/effects/video/transformeffect.cpp b/effects/video/transformeffect.cpp index 01f9de90c..c572106e5 100644 --- a/effects/video/transformeffect.cpp +++ b/effects/video/transformeffect.cpp @@ -121,13 +121,13 @@ void TransformEffect::toggle_uniform_scale(bool enabled) { scale_y->set_enabled(!enabled); } -void TransformEffect::process_gl(long frame, QOpenGLShaderProgram&, GLTextureCoords& coords) { +void TransformEffect::process_gl(double timecode, 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); + glTranslatef(position_x->get_double_value(timecode)-(parent_clip->sequence->width/2), position_y->get_double_value(timecode)-(parent_clip->sequence->height/2), 0); // anchor point - 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); + int anchor_x_offset = (anchor_x_box->get_double_value(timecode)-default_anchor_x); + int anchor_y_offset = (anchor_y_box->get_double_value(timecode)-default_anchor_y); coords.vertexTopLeftX -= anchor_x_offset; coords.vertexTopRightX -= anchor_x_offset; coords.vertexBottomLeftX -= anchor_x_offset; @@ -138,15 +138,15 @@ void TransformEffect::process_gl(long frame, QOpenGLShaderProgram&, GLTextureCoo coords.vertexBottomRightY -= anchor_y_offset; // rotation - glRotatef(rotation->get_double_value(frame), 0, 0, 1); + glRotatef(rotation->get_double_value(timecode), 0, 0, 1); // scale - float sx = scale_x->get_double_value(frame)*0.01; - float sy = (uniform_scale_field->get_bool_value(frame)) ? sx : scale_y->get_double_value(frame)*0.01; + float sx = scale_x->get_double_value(timecode)*0.01; + float sy = (uniform_scale_field->get_bool_value(timecode)) ? sx : scale_y->get_double_value(timecode)*0.01; glScalef(sx, sy, 1); // blend mode - switch (blend_mode_box->get_combo_data(frame).toInt()) { + switch (blend_mode_box->get_combo_data(timecode).toInt()) { case BLEND_MODE_NORMAL: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; @@ -166,5 +166,5 @@ void TransformEffect::process_gl(long frame, QOpenGLShaderProgram&, GLTextureCoo // opacity float color[4]; glGetFloatv(GL_CURRENT_COLOR, color); - glColor4f(1.0, 1.0, 1.0, color[3]*(opacity->get_double_value(frame)*0.01)); + glColor4f(1.0, 1.0, 1.0, color[3]*(opacity->get_double_value(timecode)*0.01)); } diff --git a/effects/video/transformeffect.h b/effects/video/transformeffect.h index c0f71a684..a882096dc 100644 --- a/effects/video/transformeffect.h +++ b/effects/video/transformeffect.h @@ -8,7 +8,7 @@ class TransformEffect : public Effect { public: TransformEffect(Clip* c); void refresh(); - void process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords); + void process_gl(double timecode, QOpenGLShaderProgram& shaders, GLTextureCoords& coords); EffectField* position_x; EffectField* position_y; diff --git a/olive.pro b/olive.pro index f318dcd99..bdfe25591 100644 --- a/olive.pro +++ b/olive.pro @@ -71,7 +71,8 @@ SOURCES += \ effects/video/shakeeffect.cpp \ effects/video/inverteffect.cpp \ ui/keyframeview.cpp \ - ui/texteditex.cpp + ui/texteditex.cpp \ + effects/video/chromakeyeffect.cpp HEADERS += \ mainwindow.h \ @@ -119,7 +120,8 @@ HEADERS += \ effects/audio/volumeeffect.h \ effects/audio/paneffect.h \ ui/keyframeview.h \ - ui/texteditex.h + ui/texteditex.h \ + effects/video/chromakeyeffect.h FORMS += \ mainwindow.ui \ diff --git a/playback/cacher.cpp b/playback/cacher.cpp index cbd64284f..fcf66f447 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -25,20 +25,21 @@ int dest_format = AV_PIX_FMT_RGBA; void apply_audio_effects(Clip* c, AVFrame* frame, int nb_bytes) { // perform all audio effects + double timecode_start = (frame->pts * av_q2d(c->stream->time_base)); + double timecode_end = timecode_start + ((double) (nb_bytes >> 1) / frame->channels / frame->sample_rate); + for (int j=0;jeffects.size();j++) { - Effect* e = c->effects.at(j); - if (e->is_enabled()) e->process_audio(frame->data[0], nb_bytes); + Effect* e = c->effects.at(j); + if (e->is_enabled()) e->process_audio(timecode_start, timecode_end, frame->data[0], nb_bytes, 2); } if (c->opening_transition != NULL) { if (c->media_type == MEDIA_TYPE_FOOTAGE) { double transition_start = (c->clip_in / c->sequence->frame_rate); double transition_end = transition_start + (c->opening_transition->length / c->sequence->frame_rate); - double range_start = (frame->pts * av_q2d(c->stream->time_base)); - double range_end = range_start + ((double) nb_bytes * 0.5 / frame->channels / frame->sample_rate); - if (range_end < transition_end) { + if (timecode_end < transition_end) { double adjustment = transition_end - transition_start; - double adjusted_range_start = (range_start - transition_start) / adjustment; - double adjusted_range_end = (range_end - transition_start) / adjustment; + double adjusted_range_start = (timecode_start - transition_start) / adjustment; + double adjusted_range_end = (timecode_end - transition_start) / adjustment; c->opening_transition->process_audio(adjusted_range_start, adjusted_range_end, frame->data[0], nb_bytes, false); } } @@ -47,12 +48,10 @@ void apply_audio_effects(Clip* c, AVFrame* frame, int nb_bytes) { if (c->media_type == MEDIA_TYPE_FOOTAGE) { double transition_end = (c->clip_in + c->getLength()) / c->sequence->frame_rate; double transition_start = transition_end - (c->closing_transition->length / c->sequence->frame_rate); - double range_start = (frame->pts * av_q2d(c->stream->time_base)); - double range_end = range_start + ((double) nb_bytes * 0.5 / frame->channels / frame->sample_rate); - if (range_start > transition_start) { + if (timecode_start > transition_start) { double adjustment = transition_end - transition_start; - double adjusted_range_start = (range_start - transition_start) / adjustment; - double adjusted_range_end = (range_end - transition_start) / adjustment; + double adjusted_range_start = (timecode_start - transition_start) / adjustment; + double adjusted_range_end = (timecode_end - transition_start) / adjustment; c->closing_transition->process_audio(adjusted_range_start, adjusted_range_end, frame->data[0], nb_bytes, true); } } @@ -176,7 +175,7 @@ void cache_audio_worker(Clip* c, Clip* nest) { } void cache_video_worker(Clip* c, long playhead, ClipCache* cache) { - cache->mutex.lock(); + cache->mutex.lock(); cache->offset = playhead; @@ -187,6 +186,7 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) { if (!c->reached_end) { while (i < c->cache_size) { retrieve_next_frame_raw_data(c, cache->frames[i]); + if (c->reached_end) break; i++; } diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 7568c9a44..1d98e162c 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -183,8 +183,9 @@ void ViewerWidget::compose_sequence(QVector& nests, bool render_audio) { QOpenGLShaderProgram shader; for (int j=0;jeffects.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, coords); + 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); + } } if (c->opening_transition != NULL) {