diff --git a/effects/paneffect.cpp b/effects/paneffect.cpp index 57269e131..e5832102f 100644 --- a/effects/paneffect.cpp +++ b/effects/paneffect.cpp @@ -30,7 +30,7 @@ void PanEffect::load(QXmlStreamReader *stream) { stream->readNext(); if (stream->isStartElement() && stream->name() == "pan") { stream->readNext(); - pan_val->set_value(stream->text().toFloat()); + pan_val->set_value(stream->text().toDouble()); } } } @@ -45,7 +45,7 @@ void PanEffect::process_audio(quint8 *samples, int nb_bytes) { 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)); - float val = qPow(pan_val->value()*0.01f, 3); + double val = qPow(pan_val->value()*0.01, 3); if (val < 0) { // affect right channel right_sample *= (1-std::abs(val)); diff --git a/effects/shakeeffect.cpp b/effects/shakeeffect.cpp index 27648768f..23e7224ab 100644 --- a/effects/shakeeffect.cpp +++ b/effects/shakeeffect.cpp @@ -38,9 +38,9 @@ ShakeEffect::ShakeEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SHAKE_EFF connect(intensity_val, SIGNAL(valueChanged()), this, SLOT(field_changed())); connect(rotation_val, SIGNAL(valueChanged()), this, SLOT(field_changed())); connect(frequency_val, SIGNAL(valueChanged()), this, SLOT(field_changed())); - connect(intensity_val, SIGNAL(valueChanged()), this, SLOT(set_values())); - connect(rotation_val, SIGNAL(valueChanged()), this, SLOT(set_values())); - connect(frequency_val, SIGNAL(valueChanged()), this, SLOT(set_values())); + connect(intensity_val, SIGNAL(valueChanged()), this, SLOT(init())); + connect(rotation_val, SIGNAL(valueChanged()), this, SLOT(init())); + connect(frequency_val, SIGNAL(valueChanged()), this, SLOT(init())); } void ShakeEffect::init() { @@ -66,11 +66,11 @@ void ShakeEffect::load(QXmlStreamReader* reader) { for (int i=0;iset_value(a.value().toFloat()); + intensity_val->set_value(a.value().toDouble()); } else if (a.name() == "rotation") { - rotation_val->set_value(a.value().toFloat()); + rotation_val->set_value(a.value().toDouble()); } else if (a.name() == "frequency") { - frequency_val->set_value(a.value().toFloat()); + frequency_val->set_value(a.value().toDouble()); } } } @@ -83,7 +83,7 @@ void ShakeEffect::save(QXmlStreamWriter* stream) { void ShakeEffect::process_gl(int*, int*) { if (shake_progress > shake_limit) { - if (intensity_val->value() > 0) { + if ((int)intensity_val->value() > 0) { prev_x = next_x; prev_y = next_y; next_x = (qrand() % (int) (intensity_val->value() * 2)) - intensity_val->value(); @@ -96,7 +96,7 @@ void ShakeEffect::process_gl(int*, int*) { offset_x = 0; offset_y = 0; } - if (rotation_val->value() > 0) { + if ((int)rotation_val->value() > 0) { prev_rot = next_rot; next_rot = (qrand() % (int) (rotation_val->value() * 2)) - rotation_val->value(); } else { diff --git a/effects/transformeffect.cpp b/effects/transformeffect.cpp index 0f1964c26..073b201ef 100644 --- a/effects/transformeffect.cpp +++ b/effects/transformeffect.cpp @@ -15,6 +15,7 @@ #include "io/media.h" #include "ui/labelslider.h" #include "ui/comboboxex.h" +#include "panels/project.h" #define BLEND_MODE_NORMAL 0 #define BLEND_MODE_SCREEN 1 @@ -68,6 +69,9 @@ TransformEffect::TransformEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_T ui_layout->addWidget(blend_mode_box, 6, 1); // set defaults + scale_y->setEnabled(false); + uniform_scale_box->setChecked(true); + blend_mode_box->setCurrentIndex(0); init(); connect(position_x, SIGNAL(valueChanged()), this, SLOT(field_changed())); @@ -85,20 +89,41 @@ TransformEffect::TransformEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_T } void TransformEffect::init() { - MediaStream* ms = parent_clip->media->get_stream_from_file_index(parent_clip->media_stream); - if (ms != NULL && parent_clip->sequence != NULL) { - position_x->set_default_value(parent_clip->sequence->width/2); - position_y->set_default_value(parent_clip->sequence->height/2); + if (parent_clip->sequence != NULL) { + double default_pos_x = parent_clip->sequence->width/2; + double default_pos_y = parent_clip->sequence->height/2; + + position_x->set_default_value(default_pos_x); + position_y->set_default_value(default_pos_y); scale_x->set_default_value(100); scale_y->set_default_value(100); - scale_y->setEnabled(false); - uniform_scale_box->setChecked(true); - default_anchor_x = ms->video_width/2; - default_anchor_y = ms->video_height/2; + + default_anchor_x = default_pos_x; + default_anchor_y = default_pos_y; + if (parent_clip->media != NULL) { + switch (parent_clip->media_type) { + case MEDIA_TYPE_FOOTAGE: + { + MediaStream* ms = static_cast(parent_clip->media)->get_stream_from_file_index(parent_clip->media_stream); + if (ms != NULL) { + default_anchor_x = ms->video_width/2; + default_anchor_y = ms->video_height/2; + } + break; + } + case MEDIA_TYPE_SEQUENCE: + { + Sequence* s = static_cast(parent_clip->media); + default_anchor_x = s->width/2; + default_anchor_y = s->height/2; + break; + } + } + } + anchor_x_box->set_default_value(default_anchor_x); anchor_y_box->set_default_value(default_anchor_y); opacity->set_default_value(100); - blend_mode_box->setCurrentIndex(0); } } @@ -122,31 +147,31 @@ void TransformEffect::load(QXmlStreamReader *stream) { stream->readNext(); if (stream->isStartElement() && stream->name() == "posx") { stream->readNext(); - position_x->set_value(stream->text().toFloat()); + position_x->set_value(stream->text().toDouble()); } else if (stream->isStartElement() && stream->name() == "posy") { stream->readNext(); - position_y->set_value(stream->text().toFloat()); + position_y->set_value(stream->text().toDouble()); } else if (stream->isStartElement() && stream->name() == "scalex") { stream->readNext(); - scale_x->set_value(stream->text().toFloat()); + scale_x->set_value(stream->text().toDouble()); } else if (stream->isStartElement() && stream->name() == "scaley") { stream->readNext(); - scale_y->set_value(stream->text().toFloat()); + scale_y->set_value(stream->text().toDouble()); } else if (stream->isStartElement() && stream->name() == "uniformscale") { stream->readNext(); uniform_scale_box->setChecked(stream->text() == "1"); } else if (stream->isStartElement() && stream->name() == "rotation") { stream->readNext(); - rotation->set_value(stream->text().toFloat()); + rotation->set_value(stream->text().toDouble()); } else if (stream->isStartElement() && stream->name() == "anchorx") { stream->readNext(); - anchor_x_box->set_value(stream->text().toFloat()); + anchor_x_box->set_value(stream->text().toDouble()); } else if (stream->isStartElement() && stream->name() == "anchory") { stream->readNext(); - anchor_y_box->set_value(stream->text().toFloat()); + anchor_y_box->set_value(stream->text().toDouble()); } else if (stream->isStartElement() && stream->name() == "opacity") { stream->readNext(); - opacity->set_value(stream->text().toFloat()); + opacity->set_value(stream->text().toDouble()); } else if (stream->isStartElement() && stream->name() == "blendmode") { stream->readNext(); blend_mode_box->setCurrentIndex(stream->text().toInt()); @@ -183,9 +208,9 @@ void TransformEffect::process_gl(int* anchor_x, int* anchor_y) { glRotatef(rotation->value(), 0, 0, 1); // scale - float sx = scale_x->value()*0.01; - float sy = (uniform_scale_box->isChecked()) ? sx : scale_y->value()*0.01; - glScalef(sx, sy, 1); + float sx = scale_x->value()*0.01; + float sy = (uniform_scale_box->isChecked()) ? sx : scale_y->value()*0.01; + glScalef(sx, sy, 1); // blend mode switch (blend_mode_box->currentData().toInt()) { diff --git a/effects/volumeeffect.cpp b/effects/volumeeffect.cpp index f3335bd0d..4e9e1cf67 100644 --- a/effects/volumeeffect.cpp +++ b/effects/volumeeffect.cpp @@ -31,7 +31,7 @@ void VolumeEffect::load(QXmlStreamReader* stream) { stream->readNext(); if (stream->isStartElement() && stream->name() == "volume") { stream->readNext(); - volume_val->set_value(stream->text().toFloat()); + volume_val->set_value(stream->text().toDouble()); } } } diff --git a/io/config.h b/io/config.h index 703abbaf8..c2b6efcf5 100644 --- a/io/config.h +++ b/io/config.h @@ -3,7 +3,7 @@ #include -#define SAVE_VERSION "180722" +#define SAVE_VERSION "180727" struct Config { Config(); diff --git a/mainwindow.cpp b/mainwindow.cpp index ad6aab62d..8e80ec67c 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -162,7 +162,7 @@ void MainWindow::on_action_Import_triggered() void MainWindow::on_actionExit_triggered() { - QApplication::quit(); + close(); } void MainWindow::on_actionAbout_triggered() diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 2c673c04e..10c8e5e65 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -29,15 +29,14 @@ EffectControls::~EffectControls() } void EffectControls::menu_select(QAction* q) { - EffectAddCommand* command = new EffectAddCommand(); + TimelineAction* ta = new TimelineAction(); for (int i=0;iget_clip(selected_clips.at(i)); - if ((clip->track < 0 && video_menu) || (clip->track >= 0 && !video_menu)) { - command->clips.append(clip); - command->effects.append(create_effect(q->data().toInt(), clip)); + if ((sequence->get_clip(selected_clips.at(i))->track < 0) == video_menu) { + ta->add_effect(sequence, selected_clips.at(i), q->data().toInt()); } } - undo_stack.push(command); + undo_stack.push(ta); + reload_clips(); project_changed = true; } @@ -194,38 +193,6 @@ bool EffectControls::is_focused() { return false; } -EffectAddCommand::EffectAddCommand() : done(false) {} - -EffectAddCommand::~EffectAddCommand() { - if (!done) { - for (int i=0;ieffects.size();j++) { - if (c->effects.at(j) == effects.at(i)) { - c->effects.removeAt(j); - break; - } - } - } - panel_effect_controls->reload_clips(); - done = false; -} - -void EffectAddCommand::redo() { - for (int i=0;ieffects.append(effects.at(i)); - } - panel_effect_controls->reload_clips(); - done = true; -} - EffectDeleteCommand::EffectDeleteCommand() : done(false) {} EffectDeleteCommand::~EffectDeleteCommand() { diff --git a/panels/effectcontrols.h b/panels/effectcontrols.h index d893c11be..ff470c265 100644 --- a/panels/effectcontrols.h +++ b/panels/effectcontrols.h @@ -44,7 +44,7 @@ private: bool video_menu; }; -class EffectAddCommand : public QUndoCommand { +/*class EffectAddCommand : public QUndoCommand { public: EffectAddCommand(); ~EffectAddCommand(); @@ -54,7 +54,7 @@ public: QVector effects; private: bool done; -}; +};*/ class EffectDeleteCommand : public QUndoCommand { public: diff --git a/panels/project.cpp b/panels/project.cpp index ccf21c663..1fea546e2 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -523,29 +523,27 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) { break; case MEDIA_TYPE_FOOTAGE: { - QString name; - QString url; - int id; - int folder; + int folder = 0; + + QTreeWidgetItem* item = new_item();//import_file(url); + Media* m = new Media(); + for (int j=0;jsave_id = attr.value().toInt(); } else if (attr.name() == "folder") { folder = attr.value().toInt(); } else if (attr.name() == "name") { - name = attr.value().toString(); + m->name = attr.value().toString(); } else if (attr.name() == "url") { - url = attr.value().toString(); + m->url = attr.value().toString(); + } else if (attr.name() == "duration") { + m->length = attr.value().toLongLong(); } } - QTreeWidgetItem* item = new_item();//import_file(url); - Media* m = new Media(); - m->url = url; - m->name = name; - m->save_id = id; - while (!(stream.name() == child_search && stream.isEndElement())) { + /*while (!(stream.name() == child_search && stream.isEndElement())) { stream.readNext(); if (stream.isStartElement()) { if (stream.name() == "video") { @@ -582,9 +580,10 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) { m->audio_tracks.append(ms); } } - } + }*/ set_media_of_tree(item, m); + if (folder == 0) { ui->treeWidget->addTopLevelItem(item); } else { @@ -659,55 +658,76 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) { } else if (attr.name() == "b") { c->color_b = attr.value().toInt(); } else if (attr.name() == "media") { + c->media_type = MEDIA_TYPE_FOOTAGE; media_id = attr.value().toInt(); } else if (attr.name() == "stream") { stream_id = attr.value().toInt(); + } else if (attr.name() == "sequence") { + c->media_type = MEDIA_TYPE_SEQUENCE; + + // since we haven't finished loading sequences, we defer linking this until later + c->media = NULL; + c->media_stream = attr.value().toInt(); + loaded_clips.append(c); } } // set media and media stream - for (int j=0;jsave_id == media_id) { - c->media = m; - c->media_stream = stream_id; - break; + /*switch (c->media_type) { + case MEDIA_TYPE_FOOTAGE: + + + break; + }*/ + if (c->media_type == MEDIA_TYPE_FOOTAGE) { + if (media_id == 0) { + c->media = NULL; + } else { + for (int j=0;jsave_id == media_id) { + c->media = m; + c->media_stream = stream_id; + break; + } + } } } // load links and effects while (!(stream.name() == "clip" && stream.isEndElement()) && !stream.atEnd()) { stream.readNext(); - if (stream.name() == "linked" && stream.isStartElement()) { - while (!(stream.name() == "linked" && stream.isEndElement()) && !stream.atEnd()) { - stream.readNext(); - if (stream.name() == "link" && stream.isStartElement()) { - for (int k=0;klinked.append(link_attr.value().toInt()); - break; + if (stream.isStartElement()) { + if (stream.name() == "linked") { + while (!(stream.name() == "linked" && stream.isEndElement()) && !stream.atEnd()) { + stream.readNext(); + if (stream.name() == "link" && stream.isStartElement()) { + for (int k=0;klinked.append(link_attr.value().toInt()); + break; + } } } } - } - } - if (stream.name() == "effects" && stream.isStartElement()) { - while (!(stream.name() == "effects" && stream.isEndElement()) && !stream.atEnd()) { - stream.readNext(); - if (stream.name() == "effect" && stream.isStartElement()) { - int effect_id = -1; - for (int j=0;jload(&stream); + c->effects.append(e); } - } - if (effect_id != -1) { - Effect* e = create_effect(effect_id, c); - e->load(&stream); - c->effects.append(e); } } } @@ -745,6 +765,8 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) { new_sequence(NULL, s, false, parent); if (open_sequence) set_sequence(s); + + loaded_sequences.append(s); } break; } @@ -774,6 +796,8 @@ void Project::load_project() { // temp variables for loading loaded_folders.clear(); loaded_media.clear(); + loaded_clips.clear(); + loaded_sequences.clear(); // find project file version cont = load_worker(file, stream, LOAD_TYPE_VERSION); @@ -803,6 +827,17 @@ void Project::load_project() { cont = load_worker(file, stream, MEDIA_TYPE_SEQUENCE); } + // attach nested sequence clips to their sequences + for (int i=0;imedia_stream == loaded_sequences.at(j)->save_id) { + loaded_clips.at(i)->media = loaded_sequences.at(j); + + break; + } + } + } + if (!cont) { QMessageBox::critical(this, "Project Load Error", "Error loading project: " + error_str, QMessageBox::Ok); } else if (stream.hasError()) { @@ -823,121 +858,138 @@ void Project::load_project() { file.close(); } -void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int type) { +void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int type, bool set_ids_only) { bool root = (parent == NULL); int len = root ? ui->treeWidget->topLevelItemCount() : parent->childCount(); for (int i=0;itreeWidget->topLevelItem(i) : parent->child(i); int item_type = get_type_from_tree(item); - if (item_type == MEDIA_TYPE_FOLDER) { - if (type == SAVE_SET_FOLDER_IDS) { - item->setData(0, Qt::UserRole + 3, folder_id); // saves a temporary ID for matching in the project file - folder_id++; - } else if (type == MEDIA_TYPE_FOLDER) { - // if we're saving folders, save the folder - stream.writeStartElement("folder"); - stream.writeAttribute("name", item->text(0)); - stream.writeAttribute("id", QString::number(item->data(0, Qt::UserRole + 3).toInt())); - if (item->parent() == NULL) { - stream.writeAttribute("parent", "0"); + if (type == item_type) { + if (item_type == MEDIA_TYPE_FOLDER) { + if (set_ids_only) { + item->setData(0, Qt::UserRole + 3, folder_id); // saves a temporary ID for matching in the project file + folder_id++; } else { - stream.writeAttribute("parent", QString::number(item->parent()->data(0, Qt::UserRole + 3).toInt())); - } - stream.writeEndElement(); - } - save_folder(stream, item, type); - } else if (type == item_type) { - int folder = root ? 0 : parent->data(0, Qt::UserRole + 3).toInt(); - if (type == MEDIA_TYPE_FOOTAGE) { - Media* m = get_media_from_tree(item); - m->save_id = media_id; - stream.writeStartElement("footage"); - stream.writeAttribute("id", QString::number(media_id)); - stream.writeAttribute("folder", QString::number(folder)); - stream.writeAttribute("name", m->name); - stream.writeAttribute("url", m->url); - stream.writeAttribute("duration", QString::number(m->length)); - for (int j=0;jvideo_tracks.size();j++) { - MediaStream* ms = m->video_tracks.at(j); - stream.writeStartElement("video"); - stream.writeAttribute("id", QString::number(ms->file_index)); - stream.writeAttribute("width", QString::number(ms->video_width)); - stream.writeAttribute("height", QString::number(ms->video_height)); - stream.writeAttribute("framerate", QString::number(ms->video_frame_rate)); - stream.writeAttribute("infinite", QString::number(ms->infinite_length)); + // if we're saving folders, save the folder + stream.writeStartElement("folder"); + stream.writeAttribute("name", item->text(0)); + stream.writeAttribute("id", QString::number(item->data(0, Qt::UserRole + 3).toInt())); + if (item->parent() == NULL) { + stream.writeAttribute("parent", "0"); + } else { + stream.writeAttribute("parent", QString::number(item->parent()->data(0, Qt::UserRole + 3).toInt())); + } stream.writeEndElement(); } - for (int j=0;jaudio_tracks.size();j++) { - MediaStream* ms = m->audio_tracks.at(j); - stream.writeStartElement("audio"); - stream.writeAttribute("id", QString::number(ms->file_index)); - stream.writeAttribute("channels", QString::number(ms->audio_channels)); - stream.writeAttribute("layout", QString::number(ms->audio_layout)); - stream.writeAttribute("frequency", QString::number(ms->audio_frequency)); + save_folder(stream, item, type, set_ids_only); + } else { + int folder = root ? 0 : parent->data(0, Qt::UserRole + 3).toInt(); + if (type == MEDIA_TYPE_FOOTAGE) { + Media* m = get_media_from_tree(item); + m->save_id = media_id; + stream.writeStartElement("footage"); + stream.writeAttribute("id", QString::number(media_id)); + stream.writeAttribute("folder", QString::number(folder)); + stream.writeAttribute("name", m->name); + stream.writeAttribute("url", m->url); + stream.writeAttribute("duration", QString::number(m->length)); + for (int j=0;jvideo_tracks.size();j++) { + MediaStream* ms = m->video_tracks.at(j); + stream.writeStartElement("video"); + stream.writeAttribute("id", QString::number(ms->file_index)); + stream.writeAttribute("width", QString::number(ms->video_width)); + stream.writeAttribute("height", QString::number(ms->video_height)); + stream.writeAttribute("framerate", QString::number(ms->video_frame_rate)); + stream.writeAttribute("infinite", QString::number(ms->infinite_length)); + stream.writeEndElement(); + } + for (int j=0;jaudio_tracks.size();j++) { + MediaStream* ms = m->audio_tracks.at(j); + stream.writeStartElement("audio"); + stream.writeAttribute("id", QString::number(ms->file_index)); + stream.writeAttribute("channels", QString::number(ms->audio_channels)); + stream.writeAttribute("layout", QString::number(ms->audio_layout)); + stream.writeAttribute("frequency", QString::number(ms->audio_frequency)); + stream.writeEndElement(); + } stream.writeEndElement(); - } - stream.writeEndElement(); - media_id++; - } else if (type == MEDIA_TYPE_SEQUENCE) { - Sequence* s = get_sequence_from_tree(item); - stream.writeStartElement("sequence"); - stream.writeAttribute("folder", QString::number(folder)); - stream.writeAttribute("name", s->name); - stream.writeAttribute("width", QString::number(s->width)); - stream.writeAttribute("height", QString::number(s->height)); - stream.writeAttribute("framerate", QString::number(s->frame_rate)); - stream.writeAttribute("afreq", QString::number(s->audio_frequency)); - stream.writeAttribute("alayout", QString::number(s->audio_layout)); - if (s == sequence) { - stream.writeAttribute("open", "1"); - } - if (s->using_workarea) { - stream.writeAttribute("workareaIn", QString::number(s->workarea_in)); - stream.writeAttribute("workareaOut", QString::number(s->workarea_out)); - } - for (int j=0;jclip_count();j++) { - Clip* c = s->get_clip(j); - if (c != NULL) { - stream.writeStartElement("clip"); // clip - stream.writeAttribute("id", QString::number(j)); - stream.writeAttribute("name", c->name); - stream.writeAttribute("clipin", QString::number(c->clip_in)); - stream.writeAttribute("in", QString::number(c->timeline_in)); - stream.writeAttribute("out", QString::number(c->timeline_out)); - stream.writeAttribute("track", QString::number(c->track)); - if (c->opening_transition != NULL) { - stream.writeAttribute("opening", QString::number(c->opening_transition->id)); + media_id++; + } else if (type == MEDIA_TYPE_SEQUENCE) { + Sequence* s = get_sequence_from_tree(item); + if (set_ids_only) { + s->save_id = sequence_id; + sequence_id++; + } else { + stream.writeStartElement("sequence"); + stream.writeAttribute("id", QString::number(s->save_id)); + stream.writeAttribute("folder", QString::number(folder)); + stream.writeAttribute("name", s->name); + stream.writeAttribute("width", QString::number(s->width)); + stream.writeAttribute("height", QString::number(s->height)); + stream.writeAttribute("framerate", QString::number(s->frame_rate)); + stream.writeAttribute("afreq", QString::number(s->audio_frequency)); + stream.writeAttribute("alayout", QString::number(s->audio_layout)); + if (s == sequence) { + stream.writeAttribute("open", "1"); } - if (c->closing_transition != NULL) { - stream.writeAttribute("closing", QString::number(c->closing_transition->id)); + if (s->using_workarea) { + stream.writeAttribute("workareaIn", QString::number(s->workarea_in)); + stream.writeAttribute("workareaOut", QString::number(s->workarea_out)); } - stream.writeAttribute("r", QString::number(c->color_r)); - stream.writeAttribute("g", QString::number(c->color_g)); - stream.writeAttribute("b", QString::number(c->color_b)); - stream.writeAttribute("media", QString::number(c->media->save_id)); - stream.writeAttribute("stream", QString::number(c->media_stream)); + for (int j=0;jclip_count();j++) { + Clip* c = s->get_clip(j); + if (c != NULL) { + stream.writeStartElement("clip"); // clip + stream.writeAttribute("id", QString::number(j)); + stream.writeAttribute("name", c->name); + stream.writeAttribute("clipin", QString::number(c->clip_in)); + stream.writeAttribute("in", QString::number(c->timeline_in)); + stream.writeAttribute("out", QString::number(c->timeline_out)); + stream.writeAttribute("track", QString::number(c->track)); + if (c->opening_transition != NULL) { + stream.writeAttribute("opening", QString::number(c->opening_transition->id)); + } + if (c->closing_transition != NULL) { + stream.writeAttribute("closing", QString::number(c->closing_transition->id)); + } + stream.writeAttribute("r", QString::number(c->color_r)); + stream.writeAttribute("g", QString::number(c->color_g)); + stream.writeAttribute("b", QString::number(c->color_b)); - stream.writeStartElement("linked"); // linked - for (int k=0;klinked.size();k++) { - stream.writeStartElement("link"); // link - stream.writeAttribute("id", QString::number(c->linked.at(k))); - stream.writeEndElement(); // link - } - stream.writeEndElement(); // linked + stream.writeAttribute("type", QString::number(c->media_type)); + switch (c->media_type) { + case MEDIA_TYPE_FOOTAGE: + stream.writeAttribute("media", QString::number(static_cast(c->media)->save_id)); + stream.writeAttribute("stream", QString::number(c->media_stream)); + break; + case MEDIA_TYPE_SEQUENCE: + stream.writeAttribute("sequence", QString::number(static_cast(c->media)->save_id)); + break; + } - stream.writeStartElement("effects"); // effects - for (int k=0;keffects.size();k++) { - stream.writeStartElement("effect"); // effect - Effect* e = c->effects.at(k); - stream.writeAttribute("id", QString::number(e->id)); - e->save(&stream); - stream.writeEndElement(); // effect + stream.writeStartElement("linked"); // linked + for (int k=0;klinked.size();k++) { + stream.writeStartElement("link"); // link + stream.writeAttribute("id", QString::number(c->linked.at(k))); + stream.writeEndElement(); // link + } + stream.writeEndElement(); // linked + + stream.writeStartElement("effects"); // effects + for (int k=0;keffects.size();k++) { + stream.writeStartElement("effect"); // effect + Effect* e = c->effects.at(k); + stream.writeAttribute("id", QString::number(e->id)); + e->save(&stream); + stream.writeEndElement(); // effect + } + stream.writeEndElement(); // effects + stream.writeEndElement(); // clip + } } - stream.writeEndElement(); // effects - stream.writeEndElement(); // clip + stream.writeEndElement(); } } - stream.writeEndElement(); } } } @@ -945,7 +997,8 @@ void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int void Project::save_project(bool autorecovery) { folder_id = 1; - media_id = 0; + media_id = 1; + sequence_id = 1; QFile file(autorecovery ? autorecovery_filename : project_url); if (!file.open(QIODevice::WriteOnly/* | QIODevice::Text*/)) { @@ -961,18 +1014,20 @@ void Project::save_project(bool autorecovery) { stream.writeTextElement("version", SAVE_VERSION); - save_folder(stream, NULL, SAVE_SET_FOLDER_IDS); + save_folder(stream, NULL, MEDIA_TYPE_FOLDER, true); stream.writeStartElement("folders"); // folders - save_folder(stream, NULL, MEDIA_TYPE_FOLDER); + save_folder(stream, NULL, MEDIA_TYPE_FOLDER, false); stream.writeEndElement(); // folders stream.writeStartElement("media"); // media - save_folder(stream, NULL, MEDIA_TYPE_FOOTAGE); + save_folder(stream, NULL, MEDIA_TYPE_FOOTAGE, false); stream.writeEndElement(); // media + save_folder(stream, NULL, MEDIA_TYPE_SEQUENCE, true); + stream.writeStartElement("sequences"); // sequences - save_folder(stream, NULL, MEDIA_TYPE_SEQUENCE); + save_folder(stream, NULL, MEDIA_TYPE_SEQUENCE, false); stream.writeEndElement();// sequences stream.writeEndElement(); // project @@ -1027,6 +1082,24 @@ void Project::add_recent_project(QString url) { save_recent_projects(); } +void Project::list_all_sequences_worker(QVector* list, QTreeWidgetItem* parent) { + int len = (parent == NULL) ? ui->treeWidget->topLevelItemCount() : parent->childCount(); + for (int i=0;itreeWidget->topLevelItem(i) : parent->child(i); + if (get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE) { + list->append(get_sequence_from_tree(item)); + } else if (get_type_from_tree(item) == MEDIA_TYPE_FOLDER) { + list_all_sequences_worker(list, item); + } + } +} + +QVector Project::list_all_project_sequences() { + QVector list; + list_all_sequences_worker(&list, NULL); + return list; +} + #define THROBBER_LIMIT 20 #define THROBBER_SIZE 50 @@ -1048,11 +1121,30 @@ void MediaThrobber::animation_update() { void MediaThrobber::stop(int icon_type) { animator.stop(); + switch (icon_type) { case ICON_TYPE_VIDEO: item->setIcon(0, QIcon(":/icons/videosource.png")); break; case ICON_TYPE_AUDIO: item->setIcon(0, QIcon(":/icons/audiosource.png")); break; case ICON_TYPE_ERROR: item->setIcon(0, QIcon::fromTheme("dialog-error")); break; } + + // re-init all effects on all clips + QVector sequences = panel_project->list_all_project_sequences(); + for (int i=0;iclip_count();j++) { + Clip* c = s->get_clip(j); + if (c != NULL) { + for (int k=0;keffects.size();k++) { + c->effects.at(k)->init(); + } + } + } + } + + // redraw clips + panel_timeline->redraw_all_clips(false); + panel_project->source_table->viewport()->update(); deleteLater(); } diff --git a/panels/project.h b/panels/project.h index ecb578bcf..4b1555884 100644 --- a/panels/project.h +++ b/panels/project.h @@ -7,6 +7,7 @@ struct Media; struct Sequence; +struct Clip; class Timeline; class Viewer; class SourceTable; @@ -21,7 +22,6 @@ class QFile; #define MEDIA_TYPE_SOLID 3 #define LOAD_TYPE_VERSION 69 -#define SAVE_SET_FOLDER_IDS 70 class TimelineAction; @@ -65,6 +65,8 @@ public: void save_recent_projects(); + QVector list_all_project_sequences(); + SourceTable* source_table; public slots: void import_dialog(); @@ -74,16 +76,20 @@ private: Ui::Project *ui; QTreeWidgetItem* new_item(); bool load_worker(QFile& f, QXmlStreamReader& stream, int type); - void save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int type); + void save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int type, bool set_ids_only); QString error_str; int folder_id; int media_id; + int sequence_id; QVector loaded_folders; QVector loaded_media; + QVector loaded_clips; + QVector loaded_sequences; QTreeWidgetItem* find_loaded_folder_by_id(int id); void add_recent_project(QString url); void get_media_from_table(QList items, QList& list, int type); void start_preview_generator(QTreeWidgetItem* item, Media* media); + void list_all_sequences_worker(QVector* list, QTreeWidgetItem* parent); private slots: void rename_media(QTreeWidgetItem* item, int column); void clear_recent_projects(); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 80f8097e8..23cb1b39e 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -21,6 +21,10 @@ #include #include +long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) { + return qRound(((double)framenumber/source_frame_rate)*target_frame_rate); +} + Timeline::Timeline(QWidget *parent) : QDockWidget(parent), selecting(false), diff --git a/panels/timeline.h b/panels/timeline.h index 8702669d1..ea5342ed6 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -17,6 +17,7 @@ struct Media; struct MediaStream; int lerp(int a, int b, double t); +long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate); struct Ghost { int clip; @@ -31,7 +32,8 @@ struct Ghost { long old_clip_in; // importing variables - Media* media; + void* media; + int media_type; int media_stream; // other variables diff --git a/playback/cacher.cpp b/playback/cacher.cpp index fb77894bf..2e126225b 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -151,7 +151,7 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) { void reset_cache(Clip* c, long target_frame) { // if we seek to a whole other place in the timeline, we'll need to reset the cache with new values - MediaStream* ms = c->media->get_stream_from_file_index(c->media_stream); + MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->media_stream); if (ms->infinite_length) { // if this clip is a still image, we only need one frame if (!c->cache_A.written) { @@ -197,9 +197,10 @@ Cacher::Cacher(Clip* c) : clip(c) {} void open_clip_worker(Clip* clip) { // opens file resource for FFmpeg and prepares Clip struct for playback - QByteArray ba = clip->media->url.toUtf8(); + Media* m = static_cast(clip->media); + QByteArray ba = m->url.toUtf8(); const char* filename = ba.constData(); - MediaStream* ms = clip->media->get_stream_from_file_index(clip->media_stream); + MediaStream* ms = m->get_stream_from_file_index(clip->media_stream); int errCode = avformat_open_input( &clip->formatCtx, @@ -361,7 +362,7 @@ void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bo void close_clip_worker(Clip* clip) { // closes ffmpeg file handle and frees any memory used for caching - MediaStream* ms = clip->media->get_stream_from_file_index(clip->media_stream); + MediaStream* ms = static_cast(clip->media)->get_stream_from_file_index(clip->media_stream); if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { sws_freeContext(clip->sws_ctx); } else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { diff --git a/playback/playback.cpp b/playback/playback.cpp index de7432326..ec30d13bb 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -77,7 +77,7 @@ void get_clip_frame(Clip* c, long playhead) { long clip_time = seconds_to_clip_frame(c, playhead_to_seconds(c, playhead)); // do we need to update the texture? - MediaStream* ms = c->media->get_stream_from_file_index(c->media_stream); + MediaStream* ms = static_cast(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; diff --git a/project/clip.cpp b/project/clip.cpp index 1f7fc3513..f09a6560b 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -12,19 +12,21 @@ extern "C" { #include } -Clip::Clip(Sequence* s) : sequence(s) { +Clip::Clip(Sequence* s) : + sequence(s), + enabled(true), + clip_in(0), + timeline_in(0), + timeline_out(0), + track(0), + undeletable(0), + texture(NULL), + opening_transition(NULL), + closing_transition(NULL), + media(NULL), + pkt(new AVPacket()) +{ reset(); - enabled = true; - clip_in = 0; - timeline_in = 0; - timeline_out = 0; - track = 0; - undeletable = 0; - texture = NULL; - opening_transition = NULL; - closing_transition = NULL; - media = NULL; - pkt = new AVPacket(); } Clip* Clip::copy(Sequence* s) { @@ -41,6 +43,7 @@ Clip* Clip::copy(Sequence* s) { copy->color_b = color_b; copy->sequence = s; copy->media = media; + copy->media_type = media_type; copy->media_stream = media_stream; for (int i=0;iinit(); + } +} + Clip::~Clip() { if (open) { close_clip(this); diff --git a/project/clip.h b/project/clip.h index a149b2fba..abbc04fd6 100644 --- a/project/clip.h +++ b/project/clip.h @@ -40,6 +40,7 @@ struct Clip ~Clip(); Clip* copy(Sequence* s); void reset(); + void refresh(); bool undeletable; // timeline variables @@ -56,7 +57,8 @@ struct Clip quint8 color_g; quint8 color_b; long getLength(); - Media* media; // attached media + void* media; // attached media + int media_type; int media_stream; Sequence* sequence; diff --git a/project/sequence.h b/project/sequence.h index 0381901d4..b8cb3d8f1 100644 --- a/project/sequence.h +++ b/project/sequence.h @@ -27,6 +27,8 @@ public: bool using_workarea; long workarea_in; long workarea_out; + + int save_id; private: QVector clips; }; diff --git a/project/undo.cpp b/project/undo.cpp index d0bb1c8b9..974b4c4b8 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -9,8 +9,10 @@ #include "project/sequence.h" #include "panels/panels.h" #include "panels/project.h" +#include "panels/effectcontrols.h" #include "playback/playback.h" #include "ui/sourcetable.h" +#include "effects/effects.h" QUndoStack undo_stack; @@ -23,6 +25,7 @@ QUndoStack undo_stack; #define TA_ADD_OUT 6 #define TA_ADD_CLIP_IN 7 #define TA_ADD_TRACK 8 +#define TA_ADD_EFFECT 9 TimelineAction::TimelineAction() : done(false), @@ -139,6 +142,10 @@ void TimelineAction::ripple(Sequence* s, long point, long length) { ripple(s, point, length, i); } +void TimelineAction::add_effect(Sequence* s, int clip, int effect) { + new_action(s, TA_ADD_EFFECT, clip, 0, effect); +} + void TimelineAction::set_in_out(Sequence* s, bool enabled, long in, long out) { change_in_out = true; change_in_out_sequence = s; @@ -211,6 +218,12 @@ void TimelineAction::undo() { case TA_ADD_TRACK: sequences.at(i)->get_clip(clips.at(i))->track -= new_values.at(i); break; + case TA_ADD_EFFECT: + Clip* c = sequences.at(i)->get_clip(clips.at(i)); + Effect* effect = c->effects.at(old_values.at(i)); + c->effects.removeAt(old_values.at(i)); + delete effect; + break; } } @@ -346,6 +359,13 @@ void TimelineAction::redo() { sequences.at(i)->get_clip(clips.at(i))->track += new_values.at(i); } break; + case TA_ADD_EFFECT: + { + Clip* c = sequences.at(i)->get_clip(clips.at(i)); + old_values[i] = c->effects.size(); + c->effects.append(create_effect(new_values.at(i), c)); + } + break; } } diff --git a/project/undo.h b/project/undo.h index 67195a8be..b66fdf075 100644 --- a/project/undo.h +++ b/project/undo.h @@ -33,6 +33,7 @@ public: void ripple(Sequence* s, long point, long length); void ripple(Sequence* s, long point, long length, QVector &ignore); void set_in_out(Sequence* s, bool enabled, long in, long out); + void add_effect(Sequence* s, int clip, int effect); void undo(); void redo(); private: diff --git a/ui/labelslider.cpp b/ui/labelslider.cpp index 88cad0e31..a5aaddcd4 100644 --- a/ui/labelslider.cpp +++ b/ui/labelslider.cpp @@ -6,19 +6,20 @@ #include #include -LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) -{ +LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) { drag_start = false; min_enabled = false; max_enabled = false; setStyleSheet("QLabel{color:#ffc000;text-decoration:underline;}QLabel:disabled{color:#808080;}"); setCursor(Qt::SizeHorCursor); internal_value = -1; - set_value(0); set_default_value(0); + set_value(0); + set = false; } -void LabelSlider::set_value(float v) { +void LabelSlider::set_value(double v) { + set = true; if (v != internal_value) { if (min_enabled && v < min_value) { internal_value = min_value; @@ -33,21 +34,25 @@ void LabelSlider::set_value(float v) { } } -float LabelSlider::value() { +bool LabelSlider::is_set() { + return set; +} + +double LabelSlider::value() { return internal_value; } -void LabelSlider::set_default_value(float v) { +void LabelSlider::set_default_value(double v) { default_value = v; - set_value(v); + if (!set) set_value(v); } -void LabelSlider::set_minimum_value(float v) { +void LabelSlider::set_minimum_value(double v) { min_value = v; min_enabled = true; } -void LabelSlider::set_maximum_value(float v) { +void LabelSlider::set_maximum_value(double v) { max_value = v; max_enabled = true; } diff --git a/ui/labelslider.h b/ui/labelslider.h index ba14d1a48..91c9b4089 100644 --- a/ui/labelslider.h +++ b/ui/labelslider.h @@ -9,29 +9,32 @@ class LabelSlider : public QLabel Q_OBJECT public: LabelSlider(QWidget* parent = 0); - void set_value(float v); - void set_default_value(float v); - void set_minimum_value(float v); - void set_maximum_value(float v); - float value(); + void set_value(double v); + void set_default_value(double v); + void set_minimum_value(double v); + void set_maximum_value(double v); + double value(); + bool is_set(); protected: void mousePressEvent(QMouseEvent *ev); void mouseMoveEvent(QMouseEvent *ev); void mouseReleaseEvent(QMouseEvent *ev); private: - float default_value; - float internal_value; - float drag_start_value; + double default_value; + double internal_value; + double drag_start_value; bool min_enabled; - float min_value; + double min_value; bool max_enabled; - float max_value; + double max_value; bool drag_start; bool drag_proc; int drag_start_x; int drag_start_y; + + bool set; signals: void valueChanged(); }; diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 7d61ee0d1..1a5bd99d4 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -113,21 +113,48 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) { } for (int i=0;iready) { - Ghost g; - g.clip = -1; - g.media = m; - g.old_clip_in = g.clip_in = 0; - g.in = entry_point; + int item_type = get_type_from_tree(item); + bool can_import = true; + + Media* m = NULL; + Sequence* s = NULL; + void* media = NULL; + long sequence_length; + + switch (item_type) { + case MEDIA_TYPE_FOOTAGE: + m = get_media_from_tree(item); + media = m; + can_import = m->ready; + break; + case MEDIA_TYPE_SEQUENCE: + s = get_sequence_from_tree(item); + sequence_length = s->getEndFrame(); + media = s; + can_import = (s != sequence && sequence_length != 0); + break; + default: + can_import = false; + } + + if (can_import) { + Ghost g; + g.clip = -1; + g.media_type = item_type; + g.trimming = false; + g.old_clip_in = g.clip_in = 0; + g.media = media; + g.in = entry_point; + + // is video source a still image? + switch (item_type) { + case MEDIA_TYPE_FOOTAGE: if (m->video_tracks.size() > 0 && m->video_tracks[0]->infinite_length && m->audio_tracks.size() == 0) { g.out = g.in + 100; } else { g.out = entry_point + m->get_length_in_frames(predicted_new_frame_rate); } - entry_point = g.out; - g.trimming = false; + for (int j=0;jaudio_tracks.size();j++) { g.track = j; g.media_stream = m->audio_tracks.at(j)->file_index; @@ -140,7 +167,20 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) { panel_timeline->ghosts.append(g); panel_timeline->video_ghosts = true; } + break; + case MEDIA_TYPE_SEQUENCE: + g.out = entry_point + sequence_length; + + g.track = -1; + panel_timeline->ghosts.append(g); + g.track = 0; + panel_timeline->ghosts.append(g); + + panel_timeline->video_ghosts = true; + panel_timeline->audio_ghosts = true; + break; } + entry_point = g.out; } } for (int i=0;ighosts.size();i++) { @@ -178,6 +218,8 @@ void TimelineWidget::dropEvent(QDropEvent* event) { QVector added_clips; Sequence* s = sequence; + + // if we're dropping into nothing, create a new sequences based on the clip being dragged if (s == NULL) { s = new Sequence(); @@ -210,41 +252,46 @@ void TimelineWidget::dropEvent(QDropEvent* event) { Clip* c = new Clip(s); c->media = g.media; + c->media_type = g.media_type; c->media_stream = g.media_stream; c->timeline_in = g.in; c->timeline_out = g.out; c->clip_in = g.clip_in; - if (c->media->video_tracks.size() == 0) { - // audio only (greenish) - c->color_r = 128; - c->color_g = 192; - c->color_b = 128; - } else if (c->media->audio_tracks.size() == 0) { - // video only (orangeish) + if (c->media_type == MEDIA_TYPE_FOOTAGE) { + Media* m = static_cast(c->media); + if (m->video_tracks.size() == 0) { + // audio only (greenish) + c->color_r = 128; + c->color_g = 192; + c->color_b = 128; + } else if (m->audio_tracks.size() == 0) { + // video only (orangeish) + c->color_r = 192; + c->color_g = 160; + c->color_b = 128; + } else { + // video and audio (blueish) + c->color_r = 128; + c->color_g = 128; + c->color_b = 192; + } + c->name = m->name; + } else if (c->media_type == MEDIA_TYPE_SEQUENCE) { + // sequence (red?ish?) c->color_r = 192; - c->color_g = 160; - c->color_b = 128; - } else { - // video and audio (blueish) - c->color_r = 128; c->color_g = 128; - c->color_b = 192; - } - c->track = g.track; - c->name = c->media->name; + c->color_b = 128; - if (c->track < 0) { - // add default video effects - c->effects.append(create_effect(VIDEO_TRANSFORM_EFFECT, c)); - } else { - // add default audio effects - c->effects.append(create_effect(AUDIO_VOLUME_EFFECT, c)); - c->effects.append(create_effect(AUDIO_PAN_EFFECT, c)); + c->name = static_cast(c->media)->name; } + c->track = g.track; + added_clips.append(c); } + ta->add_clips(s, added_clips); + // link clips from the same media for (int i=0;ilinked.append(j); } } + + // DUMB HACKY CODE THAT MIGHT NOT WORK TO ADD EFFECTS + if (c->track < 0) { + // add default video effects + ta->add_effect(s, i + sequence->clip_count(), VIDEO_TRANSFORM_EFFECT); + } else { + // add default audio effects + ta->add_effect(s, i + sequence->clip_count(), AUDIO_VOLUME_EFFECT); + ta->add_effect(s, i + sequence->clip_count(), AUDIO_PAN_EFFECT); + } } - ta->add_clips(s, added_clips); panel_timeline->ghosts.clear(); panel_timeline->importing = false; @@ -624,7 +680,15 @@ void TimelineWidget::init_ghosts() { if (panel_timeline->trim_target > -1 || panel_timeline->tool == TIMELINE_TOOL_SLIP || panel_timeline->tool == TIMELINE_TOOL_SLIDE) { // used for trim ops - g.media_length = sequence->get_clip(g.clip)->media->get_length_in_frames(sequence->frame_rate); + switch (c->media_type) { + case MEDIA_TYPE_FOOTAGE: + g.media_length = static_cast(c->media)->get_length_in_frames(sequence->frame_rate); + break; + case MEDIA_TYPE_SEQUENCE: + Sequence* s = static_cast(c->media); + g.media_length = refactor_frame_number(s->getEndFrame(), s->frame_rate, sequence->frame_rate); + break; + } } } for (int i=0;iselections.size();i++) { @@ -702,7 +766,8 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) { // validate ghosts for trimming if (panel_timeline->tool == TIMELINE_TOOL_SLIP) { - if (!c->media->get_stream_from_file_index(c->media_stream)->infinite_length) { + if (c->media_type == MEDIA_TYPE_SEQUENCE + || (c->media_type == MEDIA_TYPE_FOOTAGE && !static_cast(c->media)->get_stream_from_file_index(c->media_stream)->infinite_length)) { // prevent slip moving a clip below 0 clip_in validator = g.old_clip_in - frame_diff; if (validator < 0) frame_diff += validator; @@ -722,7 +787,8 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) { if (validator < 0) frame_diff -= validator; // prevent clip_in from going below 0 - if (!c->media->get_stream_from_file_index(c->media_stream)->infinite_length) { + if (c->media_type == MEDIA_TYPE_SEQUENCE + || (c->media_type == MEDIA_TYPE_FOOTAGE && !static_cast(c->media)->get_stream_from_file_index(c->media_stream)->infinite_length)) { validator = g.old_clip_in + frame_diff; if (validator < 0) frame_diff -= validator; } @@ -732,7 +798,8 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) { if (validator < 1) frame_diff += (1 - validator); // prevent clip length exceeding media length - if (!c->media->get_stream_from_file_index(c->media_stream)->infinite_length) { + if (c->media_type == MEDIA_TYPE_SEQUENCE + || (c->media_type == MEDIA_TYPE_FOOTAGE && !static_cast(c->media)->get_stream_from_file_index(c->media_stream)->infinite_length)) { validator = g.old_clip_in + g.ghost_length + frame_diff; if (validator > g.media_length) frame_diff -= validator - g.media_length; } @@ -1335,60 +1402,63 @@ void TimelineWidget::redraw_clips() { } } - MediaStream* ms = clip->media->get_stream_from_file_index(clip->media_stream); - if (ms == NULL) { - // draw "error lines" if media stream is missing - clip_painter.setPen(QPen(QColor(64, 64, 64), 2)); - int limit = clip_rect.width(); - int clip_height = clip_rect.bottom()-clip_rect.top(); - for (int j=-clip_height;jmedia_type == MEDIA_TYPE_FOOTAGE) { + Media* m = static_cast(clip->media); + MediaStream* ms = m->get_stream_from_file_index(clip->media_stream); + if (ms == NULL) { + // draw "error lines" if media stream is missing + clip_painter.setPen(QPen(QColor(64, 64, 64), 2)); + int limit = clip_rect.width(); + int clip_height = clip_rect.bottom()-clip_rect.top(); + for (int j=-clip_height;j clip_rect.right()) { + lines_end_y -= (clip_rect.right() - lines_end_x); + lines_end_x = clip_rect.right(); + } + clip_painter.drawLine(lines_start_x, lines_start_y, lines_end_x, lines_end_y); } - if (lines_end_x > clip_rect.right()) { - lines_end_y -= (clip_rect.right() - lines_end_x); - lines_end_x = clip_rect.right(); - } - clip_painter.drawLine(lines_start_x, lines_start_y, lines_end_x, lines_end_y); - } - } else if (ms->preview_done) { - // draw thumbnail/waveform - if (clip->track < 0) { - int thumb_y = clip_painter.fontMetrics().height()+CLIP_TEXT_PADDING+CLIP_TEXT_PADDING; - int thumb_height = clip_rect.height()-thumb_y; - int thumb_width = (thumb_height*((double)ms->video_preview.width()/(double)ms->video_preview.height())); - if (thumb_height > thumb_y && text_rect.width() + CLIP_TEXT_PADDING > thumb_width) { // at small clip heights, don't even draw it - QRect thumb_rect(thumb_x, clip_rect.y()+thumb_y, thumb_width, thumb_height); - clip_painter.drawImage(thumb_rect, ms->video_preview); - } - } else if (clip_rect.height() > TRACK_MIN_HEIGHT) { - int divider = ms->audio_channels*2; + } else if (ms->preview_done) { + // draw thumbnail/waveform + if (clip->track < 0) { + int thumb_y = clip_painter.fontMetrics().height()+CLIP_TEXT_PADDING+CLIP_TEXT_PADDING; + int thumb_height = clip_rect.height()-thumb_y; + int thumb_width = (thumb_height*((double)ms->video_preview.width()/(double)ms->video_preview.height())); + if (thumb_height > thumb_y && text_rect.width() + CLIP_TEXT_PADDING > thumb_width) { // at small clip heights, don't even draw it + QRect thumb_rect(thumb_x, clip_rect.y()+thumb_y, thumb_width, thumb_height); + clip_painter.drawImage(thumb_rect, ms->video_preview); + } + } else if (clip_rect.height() > TRACK_MIN_HEIGHT) { + int divider = ms->audio_channels*2; - clip_painter.setPen(QColor(80, 80, 80)); - int channel_height = clip_rect.height()/ms->audio_channels; - long media_length = clip->media->get_length_in_frames(clip->sequence->frame_rate); - for (int i=0;iclip_in + ((double) i/panel_timeline->zoom))/media_length) * ms->audio_preview.size())/divider)*divider; + clip_painter.setPen(QColor(80, 80, 80)); + int channel_height = clip_rect.height()/ms->audio_channels; + long media_length = m->get_length_in_frames(clip->sequence->frame_rate); + for (int i=0;iclip_in + ((double) i/panel_timeline->zoom))/media_length) * ms->audio_preview.size())/divider)*divider; - for (int j=0;jaudio_channels;j++) { - int mid = clip_rect.top()+channel_height*j+(channel_height/2); - int offset = waveform_index+(j*2); + for (int j=0;jaudio_channels;j++) { + int mid = clip_rect.top()+channel_height*j+(channel_height/2); + int offset = waveform_index+(j*2); - qint8 min = (double)ms->audio_preview.at(offset) / 128.0 * (channel_height/2); - qint8 max = (double)ms->audio_preview.at(offset+1) / 128.0 * (channel_height/2); - clip_painter.drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max); + qint8 min = (double)ms->audio_preview.at(offset) / 128.0 * (channel_height/2); + qint8 max = (double)ms->audio_preview.at(offset+1) / 128.0 * (channel_height/2); + clip_painter.drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max); - /*if (offset >= 0 && offset < ms->audio_preview.size()) { + /*if (offset >= 0 && offset < ms->audio_preview.size()) { - } else { - QMessageBox::critical(this, "AAAAAAAAAAAAA", "Here's that terrible, no-good bug again!!! I've stepped around it but plz report the following to Matt:\n\n" + QString::number(offset) + " vs " + QString::number(clip->media_stream->audio_preview.size()), QMessageBox::Ok); - break; - }*/ + } else { + QMessageBox::critical(this, "AAAAAAAAAAAAA", "Here's that terrible, no-good bug again!!! I've stepped around it but plz report the following to Matt:\n\n" + QString::number(offset) + " vs " + QString::number(clip->media_stream->audio_preview.size()), QMessageBox::Ok); + break; + }*/ + } } } } diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index dc20761ad..5884877d1 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -3,6 +3,7 @@ #include "panels/panels.h" #include "panels/viewer.h" #include "panels/timeline.h" +#include "panels/project.h" #include "project/sequence.h" #include "project/effect.h" #include "effects/transition.h" @@ -92,9 +93,10 @@ void ViewerWidget::compose_sequence(Sequence *s, bool render_audio) { Clip* c = s->get_clip(i); // if clip starts within one second and/or hasn't finished yet - if (c != NULL) { - if (c->media->ready - && c->media->get_stream_from_file_index(c->media_stream) != NULL + if (c != NULL && c->media_type == MEDIA_TYPE_FOOTAGE) { + Media* m = static_cast(c->media); + if (m->ready + && m->get_stream_from_file_index(c->media_stream) != NULL && is_clip_active(c, panel_timeline->playhead)) { // if thread is already working, we don't want to touch this, // but we also don't want to hang the UI thread @@ -126,77 +128,83 @@ void ViewerWidget::compose_sequence(Sequence *s, bool render_audio) { qDebug() << "[WARNING] Tried to display clip" << i << "but it's closed"; texture_failed = true; } else if (is_clip_active(c, panel_timeline->playhead)) { - if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - // start preparing cache - get_clip_frame(c, panel_timeline->playhead); + switch (c->media_type) { + case MEDIA_TYPE_FOOTAGE: + if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + // start preparing cache + get_clip_frame(c, panel_timeline->playhead); - if (c->texture == NULL) { - qDebug() << "[WARNING] Texture hasn't been created yet"; - texture_failed = true; - } else if (panel_timeline->playhead >= c->timeline_in) { - MediaStream* ms = c->media->get_stream_from_file_index(c->media_stream); + if (c->texture == NULL) { + qDebug() << "[WARNING] Texture hasn't been created yet"; + texture_failed = true; + } else if (panel_timeline->playhead >= c->timeline_in) { + MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->media_stream); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(1.0, 1.0, 1.0, 1.0); - glLoadIdentity(); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1.0, 1.0, 1.0, 1.0); + glLoadIdentity(); - int half_width = s->width/2; - int half_height = s->height/2; - if (flip) { - glOrtho(-half_width, half_width, -half_height, half_height, -1, 1); - } else { - glOrtho(-half_width, half_width, half_height, -half_height, -1, 1); - } - int anchor_x = ms->video_width/2; - int anchor_y = ms->video_height/2; + int half_width = s->width/2; + int half_height = s->height/2; + if (flip) { + glOrtho(-half_width, half_width, -half_height, half_height, -1, 1); + } else { + glOrtho(-half_width, half_width, half_height, -half_height, -1, 1); + } + int anchor_x = ms->video_width/2; + int anchor_y = ms->video_height/2; - // perform all transform effects - for (int j=0;jeffects.size();j++) { - if (c->effects.at(j)->is_enabled()) c->effects.at(j)->process_gl(&anchor_x, &anchor_y); - } + // perform all transform effects + for (int j=0;jeffects.size();j++) { + if (c->effects.at(j)->is_enabled()) c->effects.at(j)->process_gl(&anchor_x, &anchor_y); + } - if (c->opening_transition != NULL) { - int transition_progress = panel_timeline->playhead-c->timeline_in; - if (transition_progress < c->opening_transition->length) { - c->opening_transition->process_transition((float)transition_progress/(float)c->opening_transition->length); + if (c->opening_transition != NULL) { + int transition_progress = panel_timeline->playhead-c->timeline_in; + if (transition_progress < c->opening_transition->length) { + c->opening_transition->process_transition((float)transition_progress/(float)c->opening_transition->length); + } + } + if (c->closing_transition != NULL) { + int transition_progress = c->closing_transition->length-(panel_timeline->playhead-c->timeline_in-c->getLength()+c->closing_transition->length); + if (transition_progress < c->closing_transition->length) { + c->closing_transition->process_transition((float)transition_progress/(float)c->closing_transition->length); + } + } + + int anchor_right = ms->video_width - anchor_x; + int anchor_bottom = ms->video_height - anchor_y; + + c->texture->bind(); + + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); + glVertex2f(-anchor_x, -anchor_y); + glTexCoord2f(1.0, 0.0); + glVertex2f(anchor_right, -anchor_y); + glTexCoord2f(1.0, 1.0); + glVertex2f(anchor_right, anchor_bottom); + glTexCoord2f(0.0, 1.0); + glVertex2f(-anchor_x, anchor_bottom); + glEnd(); + + c->texture->release(); + + // perform all transform effects + for (int j=0;jeffects.size();j++) { + if (c->effects.at(j)->is_enabled()) c->effects.at(j)->post_gl(); } } - if (c->closing_transition != NULL) { - int transition_progress = c->closing_transition->length-(panel_timeline->playhead-c->timeline_in-c->getLength()+c->closing_transition->length); - if (transition_progress < c->closing_transition->length) { - c->closing_transition->process_transition((float)transition_progress/(float)c->closing_transition->length); - } - } - - int anchor_right = ms->video_width - anchor_x; - int anchor_bottom = ms->video_height - anchor_y; - - c->texture->bind(); - - glBegin(GL_QUADS); - glTexCoord2f(0.0, 0.0); - glVertex2f(-anchor_x, -anchor_y); - glTexCoord2f(1.0, 0.0); - glVertex2f(anchor_right, -anchor_y); - glTexCoord2f(1.0, 1.0); - glVertex2f(anchor_right, anchor_bottom); - glTexCoord2f(0.0, 1.0); - glVertex2f(-anchor_x, anchor_bottom); - glEnd(); - - c->texture->release(); - - // perform all transform effects - for (int j=0;jeffects.size();j++) { - if (c->effects.at(j)->is_enabled()) c->effects.at(j)->post_gl(); - } + } else if (render_audio && + c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && + c->lock.tryLock()) { + // clip is not caching, start caching audio + cache_clip(c, panel_timeline->playhead, false, false, c->reset_audio); + c->lock.unlock(); } - } else if (render_audio && - c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && - c->lock.tryLock()) { - // clip is not caching, start caching audio - cache_clip(c, panel_timeline->playhead, false, false, c->reset_audio); - c->lock.unlock(); + break; + case MEDIA_TYPE_SEQUENCE: + break; } } }