diff --git a/.travis.yml b/.travis.yml index 7a3e27080..e207f875d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ before_install: - sudo apt-get update -qq install: - - sudo apt-get -y install qt59base qt59multimedia libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev + - sudo apt-get -y install qt59base qt59multimedia libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev frei0r-plugins-dev frei0r-plugins - source /opt/qt*/bin/qt*-env.sh script: diff --git a/debian/control b/debian/control index e46c0c253..bb50de7f3 100644 --- a/debian/control +++ b/debian/control @@ -2,12 +2,12 @@ Source: olive-editor Section: video Priority: optional Maintainer: Olive Team -Build-Depends: debhelper (>=9), build-essential, qt5-default, qtmultimedia5-dev, libqt5opengl5-dev, libqt5multimedia5-plugins, libavformat-dev, libavcodec-dev, libavutil-dev, libswscale-dev, libswresample-dev, libavfilter-dev, libpostproc-dev, git +Build-Depends: debhelper (>=9), build-essential, qt5-default, qtmultimedia5-dev, libqt5opengl5-dev, libqt5multimedia5-plugins, libavformat-dev, libavcodec-dev, libavutil-dev, libswscale-dev, libswresample-dev, libavfilter-dev, libpostproc-dev, git, frei0r-plugins-dev Standards-Version: 3.9.6 Homepage: https://olivevideoeditor.org/ Package: olive-editor Architecture: any -Depends: ${misc:Depends}, ${shlibs:Depends}, libqt5multimedia5-plugins +Depends: ${misc:Depends}, ${shlibs:Depends}, libqt5multimedia5-plugins, frei0r-plugins Description: Nonlinear video editor focused on performance and simplicity diff --git a/effects/internal/frei0reffect.cpp b/effects/internal/frei0reffect.cpp new file mode 100644 index 000000000..50ccd6698 --- /dev/null +++ b/effects/internal/frei0reffect.cpp @@ -0,0 +1,176 @@ +#include "frei0reffect.h" + +#ifndef NOFREI0R + +#include +#include + +typedef f0r_instance_t (*f0rConstructFunc)(unsigned int width, unsigned int height); +typedef int (*f0rInitFunc) (); +typedef void (*f0rDeinitFunc) (); +typedef void (*f0rUpdateFunc) (f0r_instance_t instance, + double time, const uint32_t* inframe, uint32_t* outframe); +typedef void (*f0rDestructFunc)(f0r_instance_t instance); +typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info); +typedef void (*f0rSetParamValue) (f0r_instance_t instance, + f0r_param_t param, int param_index); + +Frei0rEffect::Frei0rEffect(Clip *c, const EffectMeta *em) : Effect(c, em) { + enable_image = true; + + // Windows DLL loading routine + QString dll_fn = QDir(em->path).filePath(em->filename); + + handle = LibLoad(dll_fn); + if(handle == nullptr) { + QString dll_error; + +#ifdef _WIN32 + DWORD dll_err = GetLastError(); + dll_error = QString::number(dll_err); +#elif __linux__ + dll_error = dlerror(); +#endif + qCritical() << "Failed to load Frei0r plugin" << dll_fn << "-" << dll_error; + + QString msg_err = tr("Failed to load Frei0r plugin \"%1\": %2").arg(dll_fn, dll_error); + +#ifdef _WIN32 + if (dll_err == 193) { +#ifdef _WIN64 + msg_err += "\n\n" + tr("NOTE: You can't load 32-bit Frei0r plugins into a 64-bit build of Olive. Please find a 64-bit version of this plugin or switch to a 32-bit build of Olive."); +#elif _WIN32 + msg_err += "\n\n" + tr("NOTE: You can't load 64-bit Frei0r plugins into a 32-bit build of Olive. Please find a 32-bit version of this plugin or switch to a 64-bit build of Olive."); +#endif + } +#endif + + QMessageBox::critical(nullptr, tr("Error loading Frei0r plugin"), msg_err); + + return; + } + + f0rInitFunc init = reinterpret_cast(LibAddress(handle, "f0r_init")); + init(); + + f0rConstructFunc construct = reinterpret_cast(LibAddress(handle, "f0r_construct")); + instance = construct(1920, 1080); + + f0r_plugin_info_t info; + f0rGetPluginInfo info_func = reinterpret_cast(LibAddress(handle, "f0r_get_plugin_info")); + info_func(&info); + + param_count = info.num_params; + +// qDebug() << "Frei0r Name:" << info.name; +// qDebug() << "Frei0r Param Count:" << info.num_params; +// qDebug() << "Frei0r Explanation:" << info.explanation; + + get_param_info = reinterpret_cast(LibAddress(handle, "f0r_get_param_info")); + for (int i=0;i= 0 && param_info.type <= F0R_PARAM_STRING) { + EffectRow* row = add_row(param_info.name); + switch (param_info.type) { + case F0R_PARAM_BOOL: + row->add_field(EFFECT_FIELD_BOOL, QString::number(i)); + break; + case F0R_PARAM_DOUBLE: + { + EffectField* f = row->add_field(EFFECT_FIELD_DOUBLE, QString::number(i)); + f->set_double_minimum_value(0); + f->set_double_maximum_value(100); + } + break; + case F0R_PARAM_COLOR: + row->add_field(EFFECT_FIELD_COLOR, QString::number(i)); + break; + case F0R_PARAM_POSITION: + { + EffectField* fx = row->add_field(EFFECT_FIELD_DOUBLE, QString("%1X").arg(QString::number(i))); + fx->set_double_minimum_value(0); + fx->set_double_maximum_value(100); + EffectField* fy = row->add_field(EFFECT_FIELD_DOUBLE, QString("%1Y").arg(QString::number(i))); + fy->set_double_minimum_value(0); + fy->set_double_maximum_value(100); + } + break; + case F0R_PARAM_STRING: + row->add_field(EFFECT_FIELD_STRING, QString::number(i)); + break; + } + } + } +} + +Frei0rEffect::~Frei0rEffect() { + if (handle != nullptr) { + f0rDestructFunc destruct = reinterpret_cast(LibAddress(handle, "f0r_destruct")); + destruct(instance); + + f0rDeinitFunc deinit = reinterpret_cast(LibAddress(handle, "f0r_deinit")); + deinit(); + + LibClose(handle); + } +} + +void Frei0rEffect::process_image(double timecode, uint8_t *input, uint8_t *output, int) { + f0rUpdateFunc update_func = reinterpret_cast(LibAddress(handle, "f0r_update")); + + for (int i=0;i(LibAddress(handle, "f0r_set_param_value")); + switch (param_info.type) { + case F0R_PARAM_BOOL: + { + double b = param_row->field(0)->get_bool_value(timecode); + set_param(instance, &b, i); + } + break; + case F0R_PARAM_DOUBLE: + { + double d = param_row->field(0)->get_double_value(timecode)*0.01; + set_param(instance, &d, i); + } + break; + case F0R_PARAM_COLOR: + { + QColor qcolor = param_row->field(0)->get_color_value(timecode);; + + f0r_param_color fcolor; + fcolor.r = float(qcolor.redF()); + fcolor.g = float(qcolor.greenF()); + fcolor.b = float(qcolor.blueF()); + + set_param(instance, &fcolor, i); + } + break; + case F0R_PARAM_POSITION: + { + f0r_param_position pos; + pos.x = param_row->field(0)->get_double_value(timecode); + pos.y = param_row->field(1)->get_double_value(timecode); + set_param(instance, &pos, i); + } + break; + case F0R_PARAM_STRING: + { + QByteArray bytes = param_row->field(0)->get_string_value(timecode).toUtf8(); + char* byte_data = bytes.data(); + set_param(instance, &byte_data, i); + } + break; + } + } + + update_func(instance, timecode, reinterpret_cast(input), reinterpret_cast(output)); +} + +#endif diff --git a/effects/internal/frei0reffect.h b/effects/internal/frei0reffect.h new file mode 100644 index 000000000..a9730b6c5 --- /dev/null +++ b/effects/internal/frei0reffect.h @@ -0,0 +1,31 @@ +#ifndef FREI0REFFECT_H +#define FREI0REFFECT_H + +#ifndef NOFREI0R + +#include "project/effect.h" + +#include + +#include "io/crossplatformlib.h" + +typedef void (*f0rGetParamInfo)(f0r_param_info_t * info, + int param_index ); + +class Frei0rEffect : public Effect { + Q_OBJECT +public: + Frei0rEffect(Clip* c, const EffectMeta* em); + ~Frei0rEffect(); + + virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size); +private: + ModulePtr handle; + f0r_instance_t instance; + int param_count; + f0rGetParamInfo get_param_info; +}; + +#endif + +#endif // FREI0REFFECT_H diff --git a/io/avtogl.cpp b/io/avtogl.cpp index 895612c6b..3b2e39c4e 100644 --- a/io/avtogl.cpp +++ b/io/avtogl.cpp @@ -5,15 +5,15 @@ extern "C" { } enum QOpenGLTexture::PixelFormat get_gl_pix_fmt_from_av(int format) { - switch (format) { + /*switch (format) { case AV_PIX_FMT_RGB24: return QOpenGLTexture::RGB; - } + }*/ return QOpenGLTexture::RGBA; } enum QOpenGLTexture::TextureFormat get_gl_tex_fmt_from_av(int format) { - switch (format) { + /*switch (format) { case AV_PIX_FMT_RGB24: return QOpenGLTexture::RGB8_UNorm; - } + }*/ return QOpenGLTexture::RGBA8_UNorm; } diff --git a/io/config.h b/io/config.h index 9a6806216..6ab3078bc 100644 --- a/io/config.h +++ b/io/config.h @@ -3,7 +3,7 @@ #include -#define SAVE_VERSION 190104 // YYMMDD +#define SAVE_VERSION 190120 // YYMMDD #define MIN_SAVE_VERSION 190104 // lowest compatible project version #define TIMECODE_DROP 0 @@ -63,7 +63,7 @@ struct Config { bool seek_also_selects; QString css_path; int effect_textbox_lines; - bool use_software_fallback; + bool use_software_fallback; void load(QString path); void save(QString path); diff --git a/io/crossplatformlib.cpp b/io/crossplatformlib.cpp new file mode 100644 index 000000000..217635f2a --- /dev/null +++ b/io/crossplatformlib.cpp @@ -0,0 +1,23 @@ +#include "crossplatformlib.h" + +#include + +ModulePtr LibLoad(const QString &filename) { +#ifdef _WIN32 + LPCWSTR dll_fn_w = reinterpret_cast(filename.utf16()); + return LoadLibrary(dll_fn_w); +#elif defined(__linux__) || defined(__APPLE__) + return dlopen(filename.toUtf8(), RTLD_LAZY); +#else + qWarning() << "Olive doesn't know how to open dynamic libraries on this platform, external libraries will not be functional"; + return nullptr; +#endif +} + +QStringList LibFilter() { +#ifdef _WIN32 + return QStringList("*.dll"); +#elif defined(__linux__) || defined(__APPLE__) + return {"*.so", "*.dylib"}; +#endif +} diff --git a/io/crossplatformlib.h b/io/crossplatformlib.h new file mode 100644 index 000000000..d186f554b --- /dev/null +++ b/io/crossplatformlib.h @@ -0,0 +1,21 @@ +#ifndef CROSSPLATFORMLIB_H +#define CROSSPLATFORMLIB_H + +#include + +#ifdef _WIN32 + #include + #define LibAddress GetProcAddress + #define LibClose FreeModule + #define ModulePtr HMODULE +#elif defined(__linux__) || defined(__APPLE__) + #include + #define LibAddress dlsym + #define LibClose dlclose + #define ModulePtr void* +#endif + +ModulePtr LibLoad(const QString& filename); +QStringList LibFilter(); + +#endif // CROSSPLATFORMLIB_H diff --git a/io/loadthread.cpp b/io/loadthread.cpp index 64d87ff9a..2da9aaf27 100644 --- a/io/loadthread.cpp +++ b/io/loadthread.cpp @@ -18,7 +18,6 @@ #include "debug.h" #include -#include #include struct TransitionData { @@ -35,11 +34,21 @@ LoadThread::LoadThread(LoadDialog* l, bool a) : ld(l), autorecovery(a), cancelle connect(this, SIGNAL(error()), this, SLOT(error_func())); connect(this, SIGNAL(start_create_dual_transition(const TransitionData*,Clip*,Clip*,const EffectMeta*)), this, SLOT(create_dual_transition(const TransitionData*,Clip*,Clip*,const EffectMeta*))); connect(this, SIGNAL(start_create_effect_ui(QXmlStreamReader*, Clip*, int, const QString*, const EffectMeta*, long, bool)), this, SLOT(create_effect_ui(QXmlStreamReader*, Clip*, int, const QString*, const EffectMeta*, long, bool))); + connect(this, SIGNAL(start_question(const QString&, const QString &, int)), this, SLOT(question_func(const QString &, const QString &, int))); } -const EffectMeta* get_meta_from_name(const QString& name) { +const EffectMeta* get_meta_from_name(const QString& input) { + int split_index = input.indexOf('/'); + QString category; + if (split_index > -1) { + category = input.left(split_index); + } + QString name = input.mid(split_index + 1); + for (int j=0;j SAVE_VERSION) { - if (QMessageBox::warning( - mainWindow, - tr("Version Mismatch"), - tr("This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?"), - QMessageBox::Yes, - QMessageBox::No) == QMessageBox::No) { + if (proj_version < MIN_SAVE_VERSION || proj_version > SAVE_VERSION) { + emit start_question( + tr("Version Mismatch"), + tr("This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?"), + QMessageBox::Yes | QMessageBox::No + ); + waitCond.wait(&mutex); + if (question_btn == QMessageBox::No) { show_err = false; return false; } @@ -444,11 +454,14 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { if (!found) { correct_clip->linked.removeAt(j); j--; - if (QMessageBox::warning(mainWindow, - tr("Invalid Clip Link"), - tr("This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?"), - QMessageBox::Yes, - QMessageBox::No) == QMessageBox::No) { + + emit start_question( + tr("Invalid Clip Link"), + tr("This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?"), + QMessageBox::Yes | QMessageBox::No + ); + waitCond.wait(&mutex); + if (question_btn == QMessageBox::No) { delete s; return false; } @@ -565,10 +578,14 @@ void LoadThread::run() { cont = !cancelled; // find project file version - cont = load_worker(file, stream, LOAD_TYPE_VERSION); + if (cont) { + cont = load_worker(file, stream, LOAD_TYPE_VERSION); + } // find project's internal URL - cont = load_worker(file, stream, LOAD_TYPE_URL); + if (cont) { + cont = load_worker(file, stream, LOAD_TYPE_URL); + } // load folders first if (cont) { @@ -605,7 +622,6 @@ void LoadThread::run() { xml_error = true; emit error(); cont = false; - } else { // attach nested sequence clips to their sequences for (int i=0;istart_preview_generator(loaded_media_items.at(i), true); } + } else { + error_str = tr("User aborted loading"); + emit error(); } file.close(); @@ -638,6 +657,15 @@ void LoadThread::cancel() { cancelled = true; } +void LoadThread::question_func(const QString &title, const QString &text, int buttons) { + question_btn = QMessageBox::warning( + mainWindow, + title, + text, + static_cast(buttons)); + waitCond.wakeAll(); +} + void LoadThread::error_func() { if (xml_error) { qCritical() << "Error parsing XML." << error_str; diff --git a/io/loadthread.h b/io/loadthread.h index f591fb966..3aba462ce 100644 --- a/io/loadthread.h +++ b/io/loadthread.h @@ -6,6 +6,7 @@ #include #include #include +#include class Media; struct Footage; @@ -23,12 +24,14 @@ public: void run(); void cancel(); signals: + void start_question(const QString &title, const QString &text, int buttons); void success(); void error(); void start_create_effect_ui(QXmlStreamReader* stream, Clip* c, int type, const QString *effect_name, const EffectMeta* meta, long effect_length, bool effect_enabled); void start_create_dual_transition(const TransitionData* td, Clip* primary, Clip* secondary, const EffectMeta* meta); void report_progress(int p); private slots: + void question_func(const QString &title, const QString &text, int buttons); void error_func(); void success_func(); void create_effect_ui(QXmlStreamReader* stream, Clip* c, int type, const QString *effect_name, const EffectMeta* meta, long effect_length, bool effect_enabled); @@ -67,6 +70,8 @@ private: bool cancelled; bool xml_error; + + QMessageBox::StandardButton question_btn; }; #endif // LOADTHREAD_H diff --git a/io/path.cpp b/io/path.cpp index 2d24812f0..79248904d 100644 --- a/io/path.cpp +++ b/io/path.cpp @@ -32,3 +32,12 @@ QString get_config_path() { return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); } } + +QList get_effects_paths() { + QList effects_paths; + effects_paths.append(get_app_dir() + "/effects"); + effects_paths.append(get_app_dir() + "/../share/olive-editor/effects"); + QString env_path(qgetenv("OLIVE_EFFECTS_PATH")); + if (!env_path.isEmpty()) effects_paths.append(env_path); + return effects_paths; +} diff --git a/io/path.h b/io/path.h index 9eff9033f..2e2f6d841 100644 --- a/io/path.h +++ b/io/path.h @@ -6,5 +6,6 @@ QString get_app_dir(); QString get_data_path(); QString get_config_path(); +QList get_effects_paths(); #endif // PATH_H diff --git a/main.cpp b/main.cpp index 2b15e5bac..d11e9886d 100644 --- a/main.cpp +++ b/main.cpp @@ -20,9 +20,7 @@ int main(int argc, char *argv[]) { bool launch_fullscreen = false; QString load_proj; -#ifndef NODEBUG - qInstallMessageHandler(debug_message_handler); -#endif + bool use_internal_logger = true; if (argc > 1) { for (int i=1;iaddStretch(); linear_button = new QPushButton(tr("Linear")); - linear_button->setProperty("type", KEYFRAME_TYPE_LINEAR); + linear_button->setProperty("type", EFFECT_KEYFRAME_LINEAR); linear_button->setCheckable(true); bezier_button = new QPushButton(tr("Bezier")); - bezier_button->setProperty("type", KEYFRAME_TYPE_BEZIER); + bezier_button->setProperty("type", EFFECT_KEYFRAME_BEZIER); bezier_button->setCheckable(true); hold_button = new QPushButton(tr("Hold")); - hold_button->setProperty("type", KEYFRAME_TYPE_HOLD); + hold_button->setProperty("type", EFFECT_KEYFRAME_HOLD); hold_button->setCheckable(true); center_tool_layout->addStretch(); @@ -216,11 +216,11 @@ void GraphEditor::select_all() { void GraphEditor::set_key_button_enabled(bool e, int type) { linear_button->setEnabled(e); - linear_button->setChecked(type == KEYFRAME_TYPE_LINEAR); + linear_button->setChecked(type == EFFECT_KEYFRAME_LINEAR); bezier_button->setEnabled(e); - bezier_button->setChecked(type == KEYFRAME_TYPE_BEZIER); + bezier_button->setChecked(type == EFFECT_KEYFRAME_BEZIER); hold_button->setEnabled(e); - hold_button->setChecked(type == KEYFRAME_TYPE_HOLD); + hold_button->setChecked(type == EFFECT_KEYFRAME_HOLD); } void GraphEditor::passthrough_slider_value() { diff --git a/panels/panels.cpp b/panels/panels.cpp index ddd283679..bb51363b5 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -9,6 +9,7 @@ #include "project/transition.h" #include "io/config.h" #include "grapheditor.h" +#include "project/effectloaders.h" #include "debug.h" #include diff --git a/playback/audio.cpp b/playback/audio.cpp index 19e2f656f..bfdf8972e 100644 --- a/playback/audio.cpp +++ b/playback/audio.cpp @@ -33,7 +33,7 @@ bool audio_rendering = false; bool recording = false; qint8 audio_ibuffer[audio_ibuffer_size]; -int audio_ibuffer_read = 0; +unsigned long audio_ibuffer_read = 0; long audio_ibuffer_frame = 0; double audio_ibuffer_timecode = 0; @@ -114,9 +114,9 @@ int current_audio_freq() { return audio_rendering ? sequence->audio_frequency : audio_output->format().sampleRate(); } -int get_buffer_offset_from_frame(double framerate, long frame) { +unsigned long get_buffer_offset_from_frame(double framerate, long frame) { if (frame >= audio_ibuffer_frame) { - return qFloor(((double) (frame - audio_ibuffer_frame)/framerate)*current_audio_freq())*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)*av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO); + return static_cast(((double(frame - audio_ibuffer_frame)/framerate)*current_audio_freq())*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)*av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO)); } else { qWarning() << "Invalid values passed to get_buffer_offset_from_frame"; return 0; @@ -168,7 +168,7 @@ int AudioSenderThread::send_audio_to_output(int offset, int max) { // send audio to device int actual_write = audio_io_device->write((const char*) audio_ibuffer+offset, max); - int audio_ibuffer_limit = audio_ibuffer_read + actual_write; + unsigned long audio_ibuffer_limit = audio_ibuffer_read + actual_write; // send samples to audio monitor cache // TODO make this work for the footage viewer - currently, enabling it causes crash due to an ASSERT diff --git a/playback/audio.h b/playback/audio.h index c2836a6f0..980641854 100644 --- a/playback/audio.h +++ b/playback/audio.h @@ -37,7 +37,7 @@ extern QMutex audio_write_lock; #define audio_ibuffer_size 192000 extern qint8 audio_ibuffer[audio_ibuffer_size]; -extern int audio_ibuffer_read; +extern unsigned long audio_ibuffer_read; extern long audio_ibuffer_frame; extern double audio_ibuffer_timecode; extern bool audio_scrub; @@ -51,7 +51,7 @@ bool is_audio_device_set(); void init_audio(); void stop_audio(); -int get_buffer_offset_from_frame(double framerate, long frame); +unsigned long get_buffer_offset_from_frame(double framerate, long frame); bool start_recording(); void stop_recording(); diff --git a/playback/cacher.cpp b/playback/cacher.cpp index 3066402e1..f7deb4334 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -364,7 +364,8 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { if (frame->nb_samples == 0) { break; } else { - long buffer_timeline_out = get_buffer_offset_from_frame(c->sequence->frame_rate, timeline_out); + unsigned long buffer_timeline_out = get_buffer_offset_from_frame(c->sequence->frame_rate, timeline_out); + audio_write_lock.lock(); while (c->frame_sample_index < nb_bytes @@ -767,7 +768,7 @@ void open_clip_worker(Clip* clip) { }*/ enum AVPixelFormat valid_pix_fmts[] = { - AV_PIX_FMT_RGB24, +// AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE }; diff --git a/playback/playback.cpp b/playback/playback.cpp index 4e16d8d54..4495023e3 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -255,26 +255,35 @@ void get_clip_frame(Clip* c, long playhead, bool& texture_failed) { int nb_components = av_pix_fmt_desc_get(static_cast(c->pix_fmt))->nb_components; glPixelStorei(GL_UNPACK_ROW_LENGTH, target_frame->linesize[0]/nb_components); - bool copied = false; - uint8_t* data = target_frame->data[0]; + // 2 data buffers to ping-pong between + bool using_db_1 = true; + uint8_t* data_buffer_1 = target_frame->data[0]; + uint8_t* data_buffer_2 = nullptr; + int frame_size; for (int i=0;ieffects.size();i++) { Effect* e = c->effects.at(i); - if (e->enable_image) { - if (!copied) { + if (e->enable_image && e->is_enabled()) { + if (data_buffer_1 == target_frame->data[0]) { frame_size = target_frame->linesize[0]*target_frame->height; - data = new uint8_t[frame_size]; - memcpy(data, target_frame->data[0], frame_size); - copied = true; + + data_buffer_1 = new uint8_t[frame_size]; + data_buffer_2 = new uint8_t[frame_size]; + + memcpy(data_buffer_1, target_frame->data[0], frame_size); } - e->process_image(get_timecode(c, playhead), data, frame_size); + e->process_image(get_timecode(c, playhead), using_db_1 ? data_buffer_1 : data_buffer_2, using_db_1 ? data_buffer_2 : data_buffer_1, frame_size); + using_db_1 = !using_db_1; } } - c->texture->setData(0, get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8, data); + c->texture->setData(0, get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8, using_db_1 ? data_buffer_1 : data_buffer_2); - if (copied) delete [] data; + if (data_buffer_1 != target_frame->data[0]) { + delete [] data_buffer_1; + delete [] data_buffer_2; + } glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } diff --git a/project/clip.h b/project/clip.h index 69e757d5f..99e896472 100644 --- a/project/clip.h +++ b/project/clip.h @@ -33,17 +33,17 @@ class QOpenGLTexture; struct Clip { - Clip(Sequence* s); + Clip(Sequence* s); ~Clip(); - Clip* copy(Sequence* s); - void reset_audio(); + Clip* copy(Sequence* s); + void reset_audio(); void reset(); void refresh(); - long get_clip_in_with_transition(); + long get_clip_in_with_transition(); long get_timeline_in_with_transition(); long get_timeline_out_with_transition(); long getLength(); - double getMediaFrameRate(); + double getMediaFrameRate(); long getMaximumLength(); void recalculateMaxLength(); int getWidth(); @@ -56,39 +56,39 @@ struct Clip void queue_remove_earliest(); // timeline variables (should be copied in copy()) - bool enabled; - long clip_in; - long timeline_in; - long timeline_out; - int track; + bool enabled; + long clip_in; + long timeline_in; + long timeline_out; + int track; QString name; - quint8 color_r; - quint8 color_g; - quint8 color_b; - Media* media; - int media_stream; + quint8 color_r; + quint8 color_g; + quint8 color_b; + Media* media; + int media_stream; double speed; - double cached_fr; + double cached_fr; bool reverse; bool maintain_audio_pitch; bool autoscale; // other variables (should be deep copied/duplicated in copy()) - QList effects; - QVector linked; - int opening_transition; - Transition* get_opening_transition(); - int closing_transition; - Transition* get_closing_transition(); + QList effects; + QVector linked; + int opening_transition; + Transition* get_opening_transition(); + int closing_transition; + Transition* get_closing_transition(); // media handling - AVFormatContext* formatCtx; - AVStream* stream; - AVCodec* codec; - AVCodecContext* codecCtx; - AVPacket* pkt; + AVFormatContext* formatCtx; + AVStream* stream; + AVCodec* codec; + AVCodecContext* codecCtx; + AVPacket* pkt; AVFrame* frame; - AVDictionary* opts; + AVDictionary* opts; long calculated_length; // temporary variables @@ -96,23 +96,23 @@ struct Clip bool undeletable; bool reached_end; bool pkt_written; - bool open; - bool finished_opening; - bool replaced; + bool open; + bool finished_opening; + bool replaced; bool ignore_reverse; int pix_fmt; // caching functions bool use_existing_frame; - bool multithreaded; + bool multithreaded; Cacher* cacher; - QWaitCondition can_cache; + QWaitCondition can_cache; int max_queue_size; QVector queue; QMutex queue_lock; - QMutex lock; + QMutex lock; QMutex open_lock; - int64_t last_invalid_ts; + int64_t last_invalid_ts; // converters/filters AVFilterGraph* filter_graph; @@ -121,15 +121,15 @@ struct Clip // video playback variables QOpenGLFramebufferObject** fbo; - QOpenGLTexture* texture; + QOpenGLTexture* texture; long texture_frame; // audio playback variables int64_t reverse_target; - int frame_sample_index; - int audio_buffer_write; - bool audio_reset; - bool audio_just_reset; + int frame_sample_index; + unsigned long audio_buffer_write; + bool audio_reset; + bool audio_just_reset; long audio_target_frame; }; diff --git a/project/effect.cpp b/project/effect.cpp index 8812364a3..328f4fc28 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -32,6 +32,7 @@ #include "effects/internal/vsthostwin.h" #endif #include "effects/internal/fillleftrighteffect.h" +#include "effects/internal/frei0reffect.h" #include #include @@ -49,10 +50,7 @@ bool shaders_are_enabled = true; QVector effects; Effect* create_effect(Clip* c, const EffectMeta* em) { - if (!em->filename.isEmpty()) { - // load effect from file - return new Effect(c, em); - } else if (em->internal >= 0 && em->internal < EFFECT_INTERNAL_COUNT) { + if (em->internal >= 0 && em->internal < EFFECT_INTERNAL_COUNT) { // must be an internal effect switch (em->internal) { case EFFECT_INTERNAL_TRANSFORM: return new TransformEffect(c, em); @@ -68,8 +66,14 @@ Effect* create_effect(Clip* c, const EffectMeta* em) { case EFFECT_INTERNAL_FILLLEFTRIGHT: return new FillLeftRightEffect(c, em); #ifdef _WIN32 case EFFECT_INTERNAL_VST: return new VSTHostWin(c, em); +#endif +#ifndef NOFREI0R + case EFFECT_INTERNAL_FREI0R: return new Frei0rEffect(c, em); #endif } + } else if (!em->filename.isEmpty()) { + // load effect from file + return new Effect(c, em); } else { qCritical() << "Invalid effect data"; QMessageBox::critical(mainWindow, @@ -88,180 +92,6 @@ const EffectMeta* get_internal_meta(int internal_id, int type) { return nullptr; } -void load_internal_effects() { - if (!shaders_are_enabled) qWarning() << "Shaders are disabled, some effects may be nonfunctional"; - - EffectMeta em; - - // internal effects - em.type = EFFECT_TYPE_EFFECT; - em.subtype = EFFECT_TYPE_AUDIO; - - em.name = "Volume"; - em.internal = EFFECT_INTERNAL_VOLUME; - effects.append(em); - - em.name = "Pan"; - em.internal = EFFECT_INTERNAL_PAN; - effects.append(em); - -#ifdef _WIN32 - em.name = "VST Plugin 2.x"; - em.internal = EFFECT_INTERNAL_VST; - effects.append(em); -#endif - - em.name = "Tone"; - em.internal = EFFECT_INTERNAL_TONE; - effects.append(em); - - em.name = "Noise"; - em.internal = EFFECT_INTERNAL_NOISE; - effects.append(em); - - em.name = "Fill Left/Right"; - em.internal = EFFECT_INTERNAL_FILLLEFTRIGHT; - effects.append(em); - - em.subtype = EFFECT_TYPE_VIDEO; - - em.name = "Transform"; - em.category = "Distort"; - em.internal = EFFECT_INTERNAL_TRANSFORM; - effects.append(em); - - em.name = "Corner Pin"; - em.internal = EFFECT_INTERNAL_CORNERPIN; - effects.append(em); - - /*em.name = "Mask"; - em.internal = EFFECT_INTERNAL_MASK; - effects.append(em);*/ - - em.name = "Shake"; - em.internal = EFFECT_INTERNAL_SHAKE; - effects.append(em); - - em.name = "Text"; - em.category = "Render"; - em.internal = EFFECT_INTERNAL_TEXT; - effects.append(em); - - em.name = "Timecode"; - em.internal = EFFECT_INTERNAL_TIMECODE; - effects.append(em); - - em.name = "Solid"; - em.internal = EFFECT_INTERNAL_SOLID; - effects.append(em); - - // internal transitions - em.type = EFFECT_TYPE_TRANSITION; - em.category = ""; - - em.name = "Cross Dissolve"; - em.internal = TRANSITION_INTERNAL_CROSSDISSOLVE; - effects.append(em); - - em.subtype = EFFECT_TYPE_AUDIO; - - em.name = "Linear Fade"; - em.internal = TRANSITION_INTERNAL_LINEARFADE; - effects.append(em); - - em.name = "Exponential Fade"; - em.internal = TRANSITION_INTERNAL_EXPONENTIALFADE; - effects.append(em); - - em.name = "Logarithmic Fade"; - em.internal = TRANSITION_INTERNAL_LOGARITHMICFADE; - effects.append(em); -} - -QList get_effects_paths() { - QList effects_paths; - effects_paths.append(get_app_dir() + "/effects"); - effects_paths.append(get_app_dir() + "/../share/olive-editor/effects"); - QString env_path(qgetenv("OLIVE_EFFECTS_PATH")); - if (!env_path.isEmpty()) effects_paths.append(env_path); - return effects_paths; -} - -void load_shader_effects() { - QList effects_paths = get_effects_paths(); - - for (int h=0;h entries = effects_dir.entryList(QStringList("*.xml"), QDir::Files); - for (int i=0;istart(); -} - -EffectInit::EffectInit() { - panel_effect_controls->effects_loaded.lock(); -} - -void EffectInit::run() { - qInfo() << "Initializing effects..."; - load_internal_effects(); - load_shader_effects(); - load_vst_effects(); - panel_effect_controls->effects_loaded.unlock(); - qInfo() << "Finished initializing effects"; -} - Effect::Effect(Clip* c, const EffectMeta *em) : parent_clip(c), meta(em), @@ -290,7 +120,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) : // set up UI from effect file container->setText(em->name); - if (!em->filename.isEmpty()) { + if (!em->filename.isEmpty() && em->internal == -1) { QFile effect_file(em->filename); if (effect_file.open(QFile::ReadOnly)) { QXmlStreamReader reader(&effect_file); @@ -735,7 +565,7 @@ void Effect::load(QXmlStreamReader& stream) { void Effect::custom_load(QXmlStreamReader &) {} void Effect::save(QXmlStreamWriter& stream) { - stream.writeAttribute("name", meta->name); + stream.writeAttribute("name", meta->category + "/" + meta->name); stream.writeAttribute("enabled", QString::number(is_enabled())); for (int i=0;i effects; double log_volume(double linear); -void init_effects(); Effect* create_effect(Clip* c, const EffectMeta *em); const EffectMeta* get_internal_meta(int internal_id, int type); -#define EFFECT_TYPE_INVALID 0 -#define EFFECT_TYPE_VIDEO 1 -#define EFFECT_TYPE_AUDIO 2 -#define EFFECT_TYPE_EFFECT 3 -#define EFFECT_TYPE_TRANSITION 4 +enum EffectType { + EFFECT_TYPE_INVALID, + EFFECT_TYPE_VIDEO, + EFFECT_TYPE_AUDIO, + EFFECT_TYPE_EFFECT, + EFFECT_TYPE_TRANSITION +}; -#define EFFECT_KEYFRAME_LINEAR 0 -#define EFFECT_KEYFRAME_HOLD 1 -#define EFFECT_KEYFRAME_BEZIER 2 +enum EffectKeyframeType { + EFFECT_KEYFRAME_LINEAR, + EFFECT_KEYFRAME_BEZIER, + EFFECT_KEYFRAME_HOLD +}; -#define EFFECT_INTERNAL_TRANSFORM 0 -#define EFFECT_INTERNAL_TEXT 1 -#define EFFECT_INTERNAL_SOLID 2 -#define EFFECT_INTERNAL_NOISE 3 -#define EFFECT_INTERNAL_VOLUME 4 -#define EFFECT_INTERNAL_PAN 5 -#define EFFECT_INTERNAL_TONE 6 -#define EFFECT_INTERNAL_SHAKE 7 -#define EFFECT_INTERNAL_TIMECODE 8 -#define EFFECT_INTERNAL_MASK 9 -#define EFFECT_INTERNAL_FILLLEFTRIGHT 10 -#define EFFECT_INTERNAL_VST 11 -#define EFFECT_INTERNAL_CORNERPIN 12 -#define EFFECT_INTERNAL_COUNT 13 - -#define KEYFRAME_TYPE_LINEAR 0 -#define KEYFRAME_TYPE_BEZIER 1 -#define KEYFRAME_TYPE_HOLD 2 +enum EffectInternal { + EFFECT_INTERNAL_TRANSFORM, + EFFECT_INTERNAL_TEXT, + EFFECT_INTERNAL_SOLID, + EFFECT_INTERNAL_NOISE, + EFFECT_INTERNAL_VOLUME, + EFFECT_INTERNAL_PAN, + EFFECT_INTERNAL_TONE, + EFFECT_INTERNAL_SHAKE, + EFFECT_INTERNAL_TIMECODE, + EFFECT_INTERNAL_MASK, + EFFECT_INTERNAL_FILLLEFTRIGHT, + EFFECT_INTERNAL_VST, + EFFECT_INTERNAL_CORNERPIN, + EFFECT_INTERNAL_FREI0R, + EFFECT_INTERNAL_COUNT +}; struct GLTextureCoords { int grid_size; @@ -156,7 +158,7 @@ public: const char* ffmpeg_filter; - virtual void process_image(double timecode, uint8_t* data, int size); + virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size); virtual void process_shader(double timecode, GLTextureCoords&); virtual void process_coords(double timecode, GLTextureCoords& coords, int data); virtual GLuint process_superimpose(double timecode); diff --git a/project/effectfield.cpp b/project/effectfield.cpp index c0b15c04a..942e4ede2 100644 --- a/project/effectfield.cpp +++ b/project/effectfield.cpp @@ -86,53 +86,53 @@ EffectField::EffectField(EffectRow *parent, int t, const QString &i) : connect(efc, SIGNAL(changed()), this, SLOT(ui_element_change())); } break; - } + } } double EffectField::get_validated_keyframe_handle(int key, bool post) { - int comp_key = -1; + int comp_key = -1; - // find keyframe before or after this one - for (int i=0;i keyframes.at(key).time) == post) - && (comp_key == -1 - || ((keyframes.at(i).time < keyframes.at(comp_key).time) == post))) { - // compare with next keyframe for post or previous frame for pre - comp_key = i; - } - } + // find keyframe before or after this one + for (int i=0;i keyframes.at(key).time) == post) + && (comp_key == -1 + || ((keyframes.at(i).time < keyframes.at(comp_key).time) == post))) { + // compare with next keyframe for post or previous frame for pre + comp_key = i; + } + } - double adjusted_key = post ? keyframes.at(key).post_handle_x : keyframes.at(key).pre_handle_x; + double adjusted_key = post ? keyframes.at(key).post_handle_x : keyframes.at(key).pre_handle_x; - // if this is the earliest/latest keyframe, no validation is required - if (comp_key == -1) { - return adjusted_key; - } + // if this is the earliest/latest keyframe, no validation is required + if (comp_key == -1) { + return adjusted_key; + } - double comp = keyframes.at(comp_key).time - keyframes.at(key).time; + double comp = keyframes.at(comp_key).time - keyframes.at(key).time; - // if comp keyframe is bezier, validate with its accompanying handle - if (keyframes.at(comp_key).type == KEYFRAME_TYPE_BEZIER) { - double relative_comp_handle = comp + (post ? keyframes.at(comp_key).pre_handle_x : keyframes.at(comp_key).post_handle_x); - // return an average - if ((post && keyframes.at(key).post_handle_x > relative_comp_handle) - || (!post && keyframes.at(key).pre_handle_x < relative_comp_handle)) { - adjusted_key = (adjusted_key + relative_comp_handle)*0.5; - } - } + // if comp keyframe is bezier, validate with its accompanying handle + if (keyframes.at(comp_key).type == EFFECT_KEYFRAME_BEZIER) { + double relative_comp_handle = comp + (post ? keyframes.at(comp_key).pre_handle_x : keyframes.at(comp_key).post_handle_x); + // return an average + if ((post && keyframes.at(key).post_handle_x > relative_comp_handle) + || (!post && keyframes.at(key).pre_handle_x < relative_comp_handle)) { + adjusted_key = (adjusted_key + relative_comp_handle)*0.5; + } + } - // don't let handle go beyond the compare keyframe's time - if (post == (adjusted_key > comp)) { - return comp; - } + // don't let handle go beyond the compare keyframe's time + if (post == (adjusted_key > comp)) { + return comp; + } - if (post == (adjusted_key < 0)) { - return 0; - } + if (post == (adjusted_key < 0)) { + return 0; + } - // original value is valid - return adjusted_key; + // original value is valid + return adjusted_key; } QVariant EffectField::get_previous_data() { @@ -162,7 +162,7 @@ QVariant EffectField::get_current_data() { } double EffectField::frameToTimecode(long frame) { - return ((double) frame / parent_row->parent_effect->parent_clip->sequence->frame_rate); + return (double(frame) / parent_row->parent_effect->parent_clip->sequence->frame_rate); } long EffectField::timecodeToFrame(double timecode) { @@ -242,22 +242,22 @@ QVariant EffectField::validate_keyframe_data(double timecode, bool async) { double before_dbl = before_key.data.toDouble(); double after_dbl = after_key.data.toDouble(); - if (before_key.type == KEYFRAME_TYPE_HOLD) { + if (before_key.type == EFFECT_KEYFRAME_HOLD) { // hold value = before_dbl; - } else if (before_key.type == KEYFRAME_TYPE_BEZIER || after_key.type == KEYFRAME_TYPE_BEZIER) { + } else if (before_key.type == EFFECT_KEYFRAME_BEZIER || after_key.type == EFFECT_KEYFRAME_BEZIER) { // bezier interpolation - if (before_key.type == KEYFRAME_TYPE_BEZIER && after_key.type == KEYFRAME_TYPE_BEZIER) { + if (before_key.type == EFFECT_KEYFRAME_BEZIER && after_key.type == EFFECT_KEYFRAME_BEZIER) { // cubic bezier - double t = cubic_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+get_validated_keyframe_handle(before_keyframe, true), after_key.time+get_validated_keyframe_handle(after_keyframe, false), after_key.time); + double t = cubic_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+get_validated_keyframe_handle(before_keyframe, true), after_key.time+get_validated_keyframe_handle(after_keyframe, false), after_key.time); value = cubic_from_t(before_dbl, before_dbl+before_key.post_handle_y, after_dbl+after_key.pre_handle_y, after_dbl, t); - } else if (after_key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier + } else if (after_key.type == EFFECT_KEYFRAME_LINEAR) { // quadratic bezier // last keyframe is the bezier one - double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+get_validated_keyframe_handle(before_keyframe, true), after_key.time); + double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+get_validated_keyframe_handle(before_keyframe, true), after_key.time); value = quad_from_t(before_dbl, before_dbl+before_key.post_handle_y, after_dbl, t); } else { // this keyframe is the bezier one - double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, after_key.time+get_validated_keyframe_handle(after_keyframe, false), after_key.time); + double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, after_key.time+get_validated_keyframe_handle(after_keyframe, false), after_key.time); value = quad_from_t(before_dbl, after_dbl+after_key.pre_handle_y, after_dbl, t); } } else { diff --git a/project/effectloaders.cpp b/project/effectloaders.cpp new file mode 100644 index 000000000..83616e3cd --- /dev/null +++ b/project/effectloaders.cpp @@ -0,0 +1,248 @@ +#include "effectloaders.h" + +#include "project/effect.h" +#include "project/transition.h" +#include "io/path.h" +#include "panels/panels.h" +#include "panels/effectcontrols.h" +#include "io/crossplatformlib.h" + +#include +#include + +#include + +#ifndef NOFREI0R +#include +typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info); +#endif + +void load_internal_effects() { + if (!shaders_are_enabled) qWarning() << "Shaders are disabled, some effects may be nonfunctional"; + + EffectMeta em; + + // internal effects + em.type = EFFECT_TYPE_EFFECT; + em.subtype = EFFECT_TYPE_AUDIO; + + em.name = "Volume"; + em.internal = EFFECT_INTERNAL_VOLUME; + effects.append(em); + + em.name = "Pan"; + em.internal = EFFECT_INTERNAL_PAN; + effects.append(em); + +#ifdef _WIN32 + em.name = "VST Plugin 2.x"; + em.internal = EFFECT_INTERNAL_VST; + effects.append(em); +#endif + + em.name = "Tone"; + em.internal = EFFECT_INTERNAL_TONE; + effects.append(em); + + em.name = "Noise"; + em.internal = EFFECT_INTERNAL_NOISE; + effects.append(em); + + em.name = "Fill Left/Right"; + em.internal = EFFECT_INTERNAL_FILLLEFTRIGHT; + effects.append(em); + + em.subtype = EFFECT_TYPE_VIDEO; + + em.name = "Transform"; + em.category = "Distort"; + em.internal = EFFECT_INTERNAL_TRANSFORM; + effects.append(em); + + em.name = "Corner Pin"; + em.internal = EFFECT_INTERNAL_CORNERPIN; + effects.append(em); + + /*em.name = "Mask"; + em.internal = EFFECT_INTERNAL_MASK; + effects.append(em);*/ + + em.name = "Shake"; + em.internal = EFFECT_INTERNAL_SHAKE; + effects.append(em); + + em.name = "Text"; + em.category = "Render"; + em.internal = EFFECT_INTERNAL_TEXT; + effects.append(em); + + em.name = "Timecode"; + em.internal = EFFECT_INTERNAL_TIMECODE; + effects.append(em); + + em.name = "Solid"; + em.internal = EFFECT_INTERNAL_SOLID; + effects.append(em); + + // internal transitions + em.type = EFFECT_TYPE_TRANSITION; + em.category = ""; + + em.name = "Cross Dissolve"; + em.internal = TRANSITION_INTERNAL_CROSSDISSOLVE; + effects.append(em); + + em.subtype = EFFECT_TYPE_AUDIO; + + em.name = "Linear Fade"; + em.internal = TRANSITION_INTERNAL_LINEARFADE; + effects.append(em); + + em.name = "Exponential Fade"; + em.internal = TRANSITION_INTERNAL_EXPONENTIALFADE; + effects.append(em); + + em.name = "Logarithmic Fade"; + em.internal = TRANSITION_INTERNAL_LOGARITHMICFADE; + effects.append(em); +} + +void load_shader_effects() { + QList effects_paths = get_effects_paths(); + + for (int h=0;h entries = effects_dir.entryList(QStringList("*.xml"), QDir::Files); + for (int i=0;istart(); +} + +#ifndef NOFREI0R +void load_frei0r_effects_worker(const QString& dir, EffectMeta& em, QVector& loaded_names) { + QDir search_dir(dir); + if (search_dir.exists()) { + QList entry_list = search_dir.entryList(LibFilter(), QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); + for (int j=0;j(LibAddress(effect, "f0r_get_plugin_info")); + if (get_info_func != nullptr) { + f0r_plugin_info_t info; + get_info_func(&info); + + if (!loaded_names.contains(info.name) + && info.plugin_type == F0R_PLUGIN_TYPE_FILTER + && info.color_model == F0R_COLOR_MODEL_RGBA8888) { + em.name = info.name; + em.path = dir; + em.filename = entry_list.at(j); + + effects.append(em); + } +// qDebug() << "Found:" << info.name << "by" << info.author; + } + LibClose(effect); + } +// qDebug() << search_dir.filePath(entry_list.at(j)); + } + } + } +} + +void load_frei0r_effects() { + QList effect_dirs = get_effects_paths(); + int lim = effect_dirs.size(); + + // add extra search paths for frei0r effects + for (int i=0;i loaded_names; + + // search for paths + EffectMeta em; + em.category = "Frei0r"; + em.type = EFFECT_TYPE_EFFECT; + em.subtype = EFFECT_TYPE_VIDEO; + em.internal = EFFECT_INTERNAL_FREI0R; + + for (int i=0;ieffects_loaded.lock(); +} + +void EffectInit::run() { + qInfo() << "Initializing effects..."; + load_internal_effects(); + load_shader_effects(); +#ifndef NOFREI0R + load_frei0r_effects(); +#endif + panel_effect_controls->effects_loaded.unlock(); + qInfo() << "Finished initializing effects"; +} diff --git a/project/effectloaders.h b/project/effectloaders.h new file mode 100644 index 000000000..3d88cf64e --- /dev/null +++ b/project/effectloaders.h @@ -0,0 +1,8 @@ +#ifndef EFFECTLOADERS_H +#define EFFECTLOADERS_H + +#include + +void init_effects(); + +#endif // EFFECTLOADERS_H diff --git a/project/effectrow.cpp b/project/effectrow.cpp index d40c77bcf..8a378cd51 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -197,7 +197,7 @@ void EffectRow::set_keyframe_now(ComboAction* ca) { } } if (exist_key == -1) { - key.type = (f->keyframes.size() == 0) ? KEYFRAME_TYPE_LINEAR : f->keyframes.at(closest_key).type; + key.type = (f->keyframes.size() == 0) ? EFFECT_KEYFRAME_LINEAR : f->keyframes.at(closest_key).type; key.data = f->get_current_data();//f->keyframes.at(closest_key).data; unsafe_keys[i] = f->keyframes.size(); f->keyframes.append(key); diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index be32acb53..4601e9f95 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -33,6 +33,7 @@ parts: - qtmultimedia5-dev - libavformat-dev - libavfilter-dev + - frei0r-plugins-dev stage-packages: - ffmpeg - libqt5multimedia5 @@ -40,6 +41,7 @@ parts: - libpulse0 - libslang2 - libglu1-mesa + - frei0r-plugins override-build: | qmake olive.pro make diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 30cee5707..9fb8377f1 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -238,33 +238,33 @@ void GraphView::paintEvent(QPaintEvent *) { } else { const EffectKeyframe& last_key = field->keyframes.at(sorted_keys.at(j-1)); - double pre_handle = field->get_validated_keyframe_handle(sorted_keys.at(j), false); - double last_post_handle = field->get_validated_keyframe_handle(sorted_keys.at(j-1), true); + double pre_handle = field->get_validated_keyframe_handle(sorted_keys.at(j), false); + double last_post_handle = field->get_validated_keyframe_handle(sorted_keys.at(j-1), true); - if (last_key.type == KEYFRAME_TYPE_HOLD) { + if (last_key.type == EFFECT_KEYFRAME_HOLD) { // hold p.drawLine(last_key_x, last_key_y, key_x, last_key_y); p.drawLine(key_x, last_key_y, key_x, key_y); - } else if (last_key.type == KEYFRAME_TYPE_BEZIER || key.type == KEYFRAME_TYPE_BEZIER) { + } else if (last_key.type == EFFECT_KEYFRAME_BEZIER || key.type == EFFECT_KEYFRAME_BEZIER) { QPainterPath bezier_path; bezier_path.moveTo(last_key_x, last_key_y); - if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) { + if (last_key.type == EFFECT_KEYFRAME_BEZIER && key.type == EFFECT_KEYFRAME_BEZIER) { // cubic bezier bezier_path.cubicTo( - QPointF(last_key_x+last_post_handle*zoom, last_key_y-last_key.post_handle_y*zoom), - QPointF(key_x+pre_handle*zoom, key_y-key.pre_handle_y*zoom), + QPointF(last_key_x+last_post_handle*zoom, last_key_y-last_key.post_handle_y*zoom), + QPointF(key_x+pre_handle*zoom, key_y-key.pre_handle_y*zoom), QPointF(key_x, key_y) ); - } else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier + } else if (key.type == EFFECT_KEYFRAME_LINEAR) { // quadratic bezier // last keyframe is the bezier one bezier_path.quadTo( - QPointF(last_key_x+last_post_handle*zoom, last_key_y-last_key.post_handle_y*zoom), + QPointF(last_key_x+last_post_handle*zoom, last_key_y-last_key.post_handle_y*zoom), QPointF(key_x, key_y) ); } else { // this keyframe is the bezier one bezier_path.quadTo( - QPointF(key_x+pre_handle*zoom, key_y-key.pre_handle_y*zoom), + QPointF(key_x+pre_handle*zoom, key_y-key.pre_handle_y*zoom), QPointF(key_x, key_y) ); } @@ -286,7 +286,7 @@ void GraphView::paintEvent(QPaintEvent *) { int key_x = get_screen_x(key.time); int key_y = get_screen_y(key.data.toDouble()); - if (key.type == KEYFRAME_TYPE_BEZIER) { + if (key.type == EFFECT_KEYFRAME_BEZIER) { p.setPen(Qt::gray); // pre handle line @@ -612,24 +612,24 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { && event->pos().x() <= key_x) { QRect mouse_rect(event->pos().x()-BEZIER_LINE_SIZE, event->pos().y()-BEZIER_LINE_SIZE, BEZIER_LINE_SIZE+BEZIER_LINE_SIZE, BEZIER_LINE_SIZE+BEZIER_LINE_SIZE); // NOTE: FILTHY copy/paste from paintEvent - if (last_key.type == KEYFRAME_TYPE_HOLD) { + if (last_key.type == EFFECT_KEYFRAME_HOLD) { // hold if (event->pos().y() >= last_key_y-BEZIER_LINE_SIZE && event->pos().y() <= last_key_y+BEZIER_LINE_SIZE) { // dout << "make an HOLD key on field" << i << "after key" << j; click_add = true; } - } else if (last_key.type == KEYFRAME_TYPE_BEZIER || key.type == KEYFRAME_TYPE_BEZIER) { + } else if (last_key.type == EFFECT_KEYFRAME_BEZIER || key.type == EFFECT_KEYFRAME_BEZIER) { QPainterPath bezier_path; bezier_path.moveTo(last_key_x, last_key_y); - if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) { + if (last_key.type == EFFECT_KEYFRAME_BEZIER && key.type == EFFECT_KEYFRAME_BEZIER) { // cubic bezier bezier_path.cubicTo( QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom), QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom), QPointF(key_x, key_y) ); - } else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier + } else if (key.type == EFFECT_KEYFRAME_LINEAR) { // quadratic bezier // last keyframe is the bezier one bezier_path.quadTo( QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom), diff --git a/ui/keyframedrawing.cpp b/ui/keyframedrawing.cpp index 53229e46c..2621472b7 100644 --- a/ui/keyframedrawing.cpp +++ b/ui/keyframedrawing.cpp @@ -14,16 +14,16 @@ void draw_keyframe(QPainter &p, int type, int x, int y, bool darker, int r, int p.setBrush(QColor(r, g, b)); switch (type) { - case KEYFRAME_TYPE_LINEAR: + case EFFECT_KEYFRAME_LINEAR: { QPoint points[KEYFRAME_POINT_COUNT] = {QPoint(x-KEYFRAME_SIZE, y), QPoint(x, y-KEYFRAME_SIZE), QPoint(x+KEYFRAME_SIZE, y), QPoint(x, y+KEYFRAME_SIZE)}; p.drawPolygon(points, KEYFRAME_POINT_COUNT); } break; - case KEYFRAME_TYPE_BEZIER: + case EFFECT_KEYFRAME_BEZIER: p.drawEllipse(QPoint(x, y), KEYFRAME_SIZE, KEYFRAME_SIZE); break; - case KEYFRAME_TYPE_HOLD: + case EFFECT_KEYFRAME_HOLD: p.drawRect(QRect(x - KEYFRAME_SIZE, y - KEYFRAME_SIZE, KEYFRAME_SIZE*2, KEYFRAME_SIZE*2)); break; } diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 014029b08..99ea806ae 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -50,12 +50,12 @@ void KeyframeView::show_context_menu(const QPoint& pos) { if (selected_fields.size() > 0) { QMenu menu(this); - QAction* linear = menu.addAction(tr("Linear")); - linear->setData(KEYFRAME_TYPE_LINEAR); - QAction* bezier = menu.addAction(tr("Bezier")); - bezier->setData(KEYFRAME_TYPE_BEZIER); - QAction* hold = menu.addAction(tr("Hold")); - hold->setData(KEYFRAME_TYPE_HOLD); + QAction* linear = menu.addAction(tr("Linear")); + linear->setData(EFFECT_KEYFRAME_LINEAR); + QAction* bezier = menu.addAction(tr("Bezier")); + bezier->setData(EFFECT_KEYFRAME_BEZIER); + QAction* hold = menu.addAction(tr("Hold")); + hold->setData(EFFECT_KEYFRAME_HOLD); menu.addSeparator(); menu.addAction("Graph Editor"); @@ -199,9 +199,9 @@ void KeyframeView::set_y_scroll(int s) { } void KeyframeView::resize_move(double d) { - panel_effect_controls->zoom *= d; - header->update_zoom(panel_effect_controls->zoom); - update(); + panel_effect_controls->zoom *= d; + header->update_zoom(panel_effect_controls->zoom); + update(); } void KeyframeView::mousePressEvent(QMouseEvent *event) {