diff --git a/effects/effects.h b/effects/effects.h index d5049c71d..32891fd2a 100644 --- a/effects/effects.h +++ b/effects/effects.h @@ -131,6 +131,7 @@ private: class VolumeEffect : public Effect { public: VolumeEffect(Clip* c); + void init(); void process_audio(quint8* samples, int nb_bytes); Effect* copy(Clip* c); void load(QXmlStreamReader* stream); @@ -142,6 +143,7 @@ public: class PanEffect : public Effect { public: PanEffect(Clip* c); + void init(); void process_audio(quint8* samples, int nb_bytes); Effect* copy(Clip* c); void load(QXmlStreamReader* stream); diff --git a/effects/paneffect.cpp b/effects/paneffect.cpp index e5832102f..7fc09b09c 100644 --- a/effects/paneffect.cpp +++ b/effects/paneffect.cpp @@ -19,6 +19,8 @@ PanEffect::PanEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_PAN_EFFECT) { pan_val->set_value(0); } +void PanEffect::init() {} + Effect* PanEffect::copy(Clip* c) { PanEffect* p = new PanEffect(c); p->pan_val->set_value(pan_val->value()); diff --git a/effects/volumeeffect.cpp b/effects/volumeeffect.cpp index 4e9e1cf67..62d63c067 100644 --- a/effects/volumeeffect.cpp +++ b/effects/volumeeffect.cpp @@ -20,6 +20,8 @@ VolumeEffect::VolumeEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_VOLUME_ volume_val->set_value(100); } +void VolumeEffect::init() {} + Effect* VolumeEffect::copy(Clip* c) { VolumeEffect* v = new VolumeEffect(c); v->volume_val->set_value(volume_val->value()); diff --git a/mainwindow.cpp b/mainwindow.cpp index 8e80ec67c..d243b2a4c 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -364,6 +364,7 @@ void MainWindow::on_actionProject_triggered() project_url.clear(); panel_project->new_project(); panel_timeline->redraw_all_clips(false); + panel_timeline->playhead = 0; } } diff --git a/panels/project.cpp b/panels/project.cpp index 1fea546e2..ef8dd6794 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -441,9 +441,16 @@ void Project::delete_media(QTreeWidgetItem* item) { } void Project::clear() { + // delete sequences first because it's important to close all the clips before deleting the media + QVector sequences = list_all_project_sequences(); + for (int i=0;itreeWidget->topLevelItemCount() > 0) { QTreeWidgetItem* item = ui->treeWidget->topLevelItem(0); - delete_media(item); + if (get_type_from_tree(item) != MEDIA_TYPE_SEQUENCE) delete_media(item); // already deleted delete item; } } @@ -490,13 +497,15 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) { break; } + show_err = true; + while (!stream.atEnd()) { stream.readNextStartElement(); if (stream.name() == root_search) { if (type == LOAD_TYPE_VERSION) { if (stream.readElementText() != SAVE_VERSION) { if (QMessageBox::warning(this, "Version Mismatch", "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) { - error_str = "Incompatible project version."; + show_err = false; return false; } } @@ -600,7 +609,6 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) { { QTreeWidgetItem* parent = NULL; Sequence* s = new Sequence(); - bool open_sequence = false; // load attributes about sequence for (int j=0;j 0) parent = find_loaded_folder_by_id(folder); + } else if (attr.name() == "id") { + s->save_id = attr.value().toInt(); } else if (attr.name() == "width") { s->width = attr.value().toInt(); } else if (attr.name() == "height") { @@ -621,7 +631,7 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) { } else if (attr.name() == "alayout") { s->audio_layout = attr.value().toInt(); } else if (attr.name() == "open") { - open_sequence = true; + open_seq = s; } else if (attr.name() == "workareaIn") { s->using_workarea = true; s->workarea_in = attr.value().toLong(); @@ -764,8 +774,6 @@ 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; @@ -792,12 +800,14 @@ void Project::load_project() { bool cont = false; error_str.clear(); + show_err = true; // temp variables for loading loaded_folders.clear(); loaded_media.clear(); loaded_clips.clear(); loaded_sequences.clear(); + open_seq = NULL; // find project file version cont = load_worker(file, stream, LOAD_TYPE_VERSION); @@ -832,14 +842,14 @@ void Project::load_project() { for (int j=0;jmedia_stream == loaded_sequences.at(j)->save_id) { loaded_clips.at(i)->media = loaded_sequences.at(j); - + loaded_clips.at(i)->refresh(); break; } } } if (!cont) { - QMessageBox::critical(this, "Project Load Error", "Error loading project: " + error_str, QMessageBox::Ok); + if (show_err) QMessageBox::critical(this, "Project Load Error", "Error loading project: " + error_str, QMessageBox::Ok); } else if (stream.hasError()) { qDebug() << "[ERROR] Error parsing XML." << stream.errorString(); QMessageBox::critical(this, "XML Parsing Error", "Couldn't load '" + project_url + "'. " + stream.errorString(), QMessageBox::Ok); @@ -847,6 +857,8 @@ void Project::load_project() { } if (cont) { + if (open_seq != NULL) set_sequence(open_seq); + panel_timeline->redraw_all_clips(false); project_changed = false; } else { diff --git a/panels/project.h b/panels/project.h index 4b1555884..8d4d202fb 100644 --- a/panels/project.h +++ b/panels/project.h @@ -77,10 +77,12 @@ private: QTreeWidgetItem* new_item(); bool load_worker(QFile& f, QXmlStreamReader& stream, int type); void save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int type, bool set_ids_only); + bool show_err; QString error_str; int folder_id; int media_id; int sequence_id; + Sequence* open_seq; QVector loaded_folders; QVector loaded_media; QVector loaded_clips; diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 23cb1b39e..ee53c4cac 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -22,36 +22,39 @@ #include long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) { + if (source_frame_rate == target_frame_rate) return framenumber; return qRound(((double)framenumber/source_frame_rate)*target_frame_rate); } Timeline::Timeline(QWidget *parent) : QDockWidget(parent), - selecting(false), - moving_init(false), - moving_proc(false), - splitting(false), - importing(false), - playing(false), - trim_in_point(false), - snapped(false), - rect_select_init(false), - rect_select_proc(false), - snapping(true), - last_frame(0), playhead(0), - snap_point(0), + playing(false), cursor_frame(0), cursor_track(0), - trim_target(-1), zoom(1.0), - ui(new Ui::Timeline) + snapping(true), + snapped(false), + snap_point(0), + selecting(false), + rect_select_init(false), + rect_select_proc(false), + moving_init(false), + moving_proc(false), + trim_target(-1), + trim_in_point(false), + splitting(false), + importing(false), + ui(new Ui::Timeline), + last_frame(0) { ui->setupUi(this); ui->video_area->bottom_align = true; + ui->splitter->setOpaqueResize(false); + tool_buttons.append(ui->toolArrowButton); tool_buttons.append(ui->toolEditButton); tool_buttons.append(ui->toolRippleButton); @@ -125,10 +128,7 @@ void Timeline::reset_all_audio() { for (int i=0;iclip_count();i++) { Clip* c = sequence->get_clip(i); if (c != NULL) { - c->reset_audio = true; - c->frame_sample_index = 0; - c->audio_buffer_write = 0; - c->reached_end = false; + c->reset_audio(); } } } diff --git a/playback/cacher.cpp b/playback/cacher.cpp index 2e126225b..3995832f7 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -6,6 +6,7 @@ #include "playback/audio.h" #include "playback/playback.h" #include "effects/effects.h" +#include "panels/timeline.h" extern "C" { #include @@ -26,9 +27,17 @@ void apply_audio_effects(Clip* c, AVFrame* frame, int nb_bytes) { } } -void cache_audio_worker(Clip* c) { +void cache_audio_worker(Clip* c, Clip* nest) { int written = 0; int max_write = 16384; + + long timeline_in = c->timeline_in; + long timeline_out = c->timeline_out; + if (nest != NULL) { + timeline_in = refactor_frame_number(timeline_in, c->sequence->frame_rate, sequence->frame_rate) + nest->timeline_in; + timeline_out = refactor_frame_number(timeline_out, c->sequence->frame_rate, sequence->frame_rate) + nest->timeline_in; + } + while (written < max_write) { // gets one frame worth of audio and sends it to the audio buffer AVFrame* frame = c->cache_A.frames[0]; @@ -48,7 +57,7 @@ void cache_audio_worker(Clip* c) { if (c->audio_just_reset) { // get precise sample offset for the elected clip_in from this audio frame float target_sts = 0; - if (c->audio_target_frame < c->timeline_in) { + if (c->audio_target_frame < timeline_in) { target_sts = clip_frame_to_seconds(c, c->clip_in); } else { target_sts = playhead_to_seconds(c, c->audio_target_frame); @@ -73,7 +82,7 @@ void cache_audio_worker(Clip* c) { int half_buffer = (audio_ibuffer_size/2); if (c->audio_buffer_write == 0) { - c->audio_buffer_write = get_buffer_offset_from_frame(qMax(c->timeline_in, c->audio_target_frame)); + c->audio_buffer_write = get_buffer_offset_from_frame(qMax(timeline_in, c->audio_target_frame)); int offset = audio_ibuffer_read - c->audio_buffer_write; if (offset > 0) { c->audio_buffer_write += offset; @@ -100,7 +109,7 @@ void cache_audio_worker(Clip* c) { } while (c->frame_sample_index < nb_bytes) { - if (c->audio_buffer_write >= audio_ibuffer_read+half_buffer || c->audio_buffer_write >= get_buffer_offset_from_frame(c->timeline_out)) { + if (c->audio_buffer_write >= audio_ibuffer_read+half_buffer || c->audio_buffer_write >= get_buffer_offset_from_frame(timeline_out)) { written = max_write; break; } else { @@ -110,6 +119,7 @@ void cache_audio_worker(Clip* c) { qint16 new_sample = (qint16) ((frame->data[0][c->frame_sample_index+1] & 0xFF) << 8 | (frame->data[0][c->frame_sample_index] & 0xFF)); qint32 mixed_sample; + // peak handling mixed_sample = old_sample + new_sample; if (mixed_sample > INT16_MAX) { mixed_sample = INT16_MAX; @@ -329,7 +339,7 @@ void open_clip_worker(Clip* clip) { clip->cache_A.frames[0]->sample_rate = clip->sequence->audio_frequency; av_frame_make_writable(clip->cache_A.frames[0]); - clip->reset_audio = true; + clip->audio_reset = true; } clip->frame = av_frame_alloc(); @@ -339,11 +349,11 @@ void open_clip_worker(Clip* clip) { qDebug() << "[INFO] Clip opened on track" << clip->track; } -void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bool reset) { +void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bool reset, Clip* nest) { if (reset) { // note: for video, playhead is in "internal clip" frames - for audio, it's the timeline playhead reset_cache(clip, playhead); - clip->reset_audio = false; + clip->audio_reset = false; } if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { @@ -356,7 +366,7 @@ void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bo cache_video_worker(clip, playhead, &clip->cache_B); } } else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - cache_audio_worker(clip); + cache_audio_worker(clip, nest); } } @@ -401,7 +411,7 @@ void Cacher::run() { if (!caching) { break; } else { - cache_clip_worker(clip, playhead, write_A, write_B, reset); + cache_clip_worker(clip, playhead, write_A, write_B, reset, nest); } } diff --git a/playback/cacher.h b/playback/cacher.h index 1658c4c1c..42ed0dfd7 100644 --- a/playback/cacher.h +++ b/playback/cacher.h @@ -19,13 +19,14 @@ public: bool write_A; bool write_B; bool reset; + Clip* nest; private: Clip* clip; }; void open_clip_worker(Clip* clip); -void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bool reset); +void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bool reset, Clip *nest); void close_clip_worker(Clip* clip); #endif // CACHER_H diff --git a/playback/playback.cpp b/playback/playback.cpp index ec30d13bb..7097deaf4 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -59,16 +59,17 @@ void close_clip(Clip* clip) { } } -void cache_clip(Clip* clip, long playhead, bool write_A, bool write_B, bool reset) { +void cache_clip(Clip* clip, long playhead, bool write_A, bool write_B, bool reset, Clip* nest) { if (clip->multithreaded) { clip->cacher->playhead = playhead; clip->cacher->write_A = write_A; clip->cacher->write_B = write_B; clip->cacher->reset = reset; + clip->cacher->nest = nest; clip->can_cache.wakeAll(); } else { - cache_clip_worker(clip, playhead, write_A, write_B, reset); + cache_clip_worker(clip, playhead, write_A, write_B, reset, nest); } } @@ -90,7 +91,7 @@ void get_clip_frame(Clip* c, long playhead) { } else if (c->multithreaded) { if (c->lock.tryLock()) { // grab image (multi-threaded) - cache_clip(c, 0, false, false, true); + cache_clip(c, 0, false, false, true, NULL); c->lock.unlock(); } } else { @@ -146,7 +147,7 @@ void get_clip_frame(Clip* c, long playhead) { // ...otherwise start at the end of the current cache playhead = cache_offset + c->cache_size; } - cache_clip(c, playhead, write_A, write_B, cache_needs_reset); + cache_clip(c, playhead, write_A, write_B, cache_needs_reset, NULL); } c->lock.unlock(); } diff --git a/playback/playback.h b/playback/playback.h index 0604c5a49..25aa87c37 100644 --- a/playback/playback.h +++ b/playback/playback.h @@ -12,7 +12,7 @@ struct AVFrame; extern bool texture_failed; void open_clip(Clip* clip, bool multithreaded); -void cache_clip(Clip* clip, long playhead, bool write_A, bool write_B, bool reset); +void cache_clip(Clip* clip, long playhead, bool write_A, bool write_B, bool reset, Clip *nest); void close_clip(Clip* clip); void cache_audio_worker(Clip* c, bool write_A); void cache_video_worker(Clip* c, long playhead, ClipCache* cache); diff --git a/project/clip.cpp b/project/clip.cpp index f09a6560b..261b4eb11 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -5,6 +5,8 @@ #include "io/media.h" #include "playback/playback.h" #include "playback/cacher.h" +#include "panels/project.h" +#include "project/sequence.h" #include @@ -20,11 +22,11 @@ Clip::Clip(Sequence* s) : timeline_out(0), track(0), undeletable(0), - texture(NULL), + media(NULL), opening_transition(NULL), closing_transition(NULL), - media(NULL), - pkt(new AVPacket()) + pkt(new AVPacket()), + texture(NULL) { reset(); } @@ -69,7 +71,7 @@ void Clip::reset() { cache_A.unread = false; cache_B.unread = false; reached_end = false; - reset_audio = false; + audio_reset = false; frame_sample_index = false; audio_buffer_write = false; need_new_audio_frame = false; @@ -83,6 +85,26 @@ void Clip::reset() { cache_B.frames = NULL; } +void Clip::reset_audio() { + switch (media_type) { + case MEDIA_TYPE_FOOTAGE: + audio_reset = true; + frame_sample_index = 0; + audio_buffer_write = 0; + reached_end = false; + break; + case MEDIA_TYPE_SEQUENCE: + { + Sequence* nested_sequence = static_cast(media); + for (int i=0;iclip_count();i++) { + Clip* c = nested_sequence->get_clip(i); + if (c != NULL) c->reset_audio(); + } + } + break; + } +} + void Clip::refresh() { // reinitializes all effects... just in case for (int i=0;iis_enabled()) effects.at(j)->process_gl(anchor_x, anchor_y); + } + + if (opening_transition != NULL) { + int transition_progress = playhead - timeline_in; + if (transition_progress < opening_transition->length) { + opening_transition->process_transition((double)transition_progress/(double)opening_transition->length); + } + } + if (closing_transition != NULL) { + int transition_progress = closing_transition->length - (playhead - timeline_in - getLength() + closing_transition->length); + if (transition_progress < closing_transition->length) { + closing_transition->process_transition((double)transition_progress/(double)closing_transition->length); + } + } +} + Clip::~Clip() { if (open) { close_clip(this); diff --git a/project/clip.h b/project/clip.h index abbc04fd6..cd8365635 100644 --- a/project/clip.h +++ b/project/clip.h @@ -39,20 +39,23 @@ struct Clip Clip(Sequence* s); ~Clip(); Clip* copy(Sequence* s); + void reset_audio(); void reset(); void refresh(); - bool undeletable; + void run_video_pre_effect_stack(long playhead, int *anchor_x, int *anchor_y); // timeline variables + Sequence* sequence; bool enabled; + long clip_in; + long timeline_in; + long timeline_out; + int track; + bool undeletable; int load_id; QString name; long get_timeline_in_with_transition(); long get_timeline_out_with_transition(); - long timeline_in; - long timeline_out; - long clip_in; - int track; quint8 color_r; quint8 color_g; quint8 color_b; @@ -60,7 +63,6 @@ struct Clip void* media; // attached media int media_type; int media_stream; - Sequence* sequence; // other variables (should be "duplicated" in copy()) QList effects; @@ -100,7 +102,7 @@ struct Clip bool need_new_audio_frame; int frame_sample_index; int audio_buffer_write; - bool reset_audio; + bool audio_reset; bool audio_just_reset; long audio_target_frame; }; diff --git a/project/effect.cpp b/project/effect.cpp index 04f1a7b6d..10f39325b 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -28,7 +28,7 @@ Effect::Effect(Clip* c, int t, int i) : parent_clip(c), type(t), id(i) { } void Effect::init() { - qDebug() << "WRONG" << type << id; + qDebug() << "[WARNING] Tried to init base Effect class - type:" << type << "- id:" << id; } void Effect::field_changed() { diff --git a/project/undo.cpp b/project/undo.cpp index 974b4c4b8..35bbf7f16 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -486,7 +486,7 @@ void LinkCommand::redo() { } } -CheckboxCommand::CheckboxCommand(QCheckBox* b) : box(b), done(true), checked(box->isChecked()) {} +CheckboxCommand::CheckboxCommand(QCheckBox* b) : box(b), checked(box->isChecked()), done(true) {} CheckboxCommand::~CheckboxCommand() {} diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 1a5bd99d4..616146789 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -76,7 +76,9 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) { bool got_audio_values = false; for (int i=0;iready) { if (!got_video_values) { @@ -101,9 +103,24 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) { break; } } - if (got_video_values && got_audio_values) break; } } + break; + case MEDIA_TYPE_SEQUENCE: + { + Sequence* s = get_sequence_from_tree(item); + predicted_video_width = s->width; + predicted_video_height = s->height; + predicted_new_frame_rate = s->frame_rate; + predicted_audio_freq = s->audio_frequency; + predicted_audio_layout = s->audio_layout; + + got_video_values = true; + got_audio_values = true; + } + break; + } + if (got_video_values && got_audio_values) break; } } else { entry_point = panel_timeline->getFrameFromScreenPoint(pos.x()); @@ -130,6 +147,7 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) { case MEDIA_TYPE_SEQUENCE: s = get_sequence_from_tree(item); sequence_length = s->getEndFrame(); + if (sequence != NULL) sequence_length = refactor_frame_number(sequence_length, s->frame_rate, sequence->frame_rate); media = s; can_import = (s != sequence && sequence_length != 0); break; @@ -305,11 +323,11 @@ void TimelineWidget::dropEvent(QDropEvent* event) { // 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); + ta->add_effect(s, i + s->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_effect(s, i + s->clip_count(), AUDIO_VOLUME_EFFECT); + ta->add_effect(s, i + s->clip_count(), AUDIO_PAN_EFFECT); } } diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 5884877d1..3f6bc438e 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -20,12 +20,13 @@ extern "C" { #include } -ViewerWidget::ViewerWidget(QWidget *parent) : QOpenGLWidget(parent) { - multithreaded = true; - enable_paint = true; - force_audio = false; - flip = false; - +ViewerWidget::ViewerWidget(QWidget *parent) : + QOpenGLWidget(parent), + multithreaded(true), + force_audio(false), + enable_paint(true), + flip(false) +{ QSurfaceFormat format; format.setDepthBufferSize(24); setFormat(format); @@ -86,24 +87,52 @@ void ViewerWidget::paintEvent(QPaintEvent *e) { if (enable_paint) QOpenGLWidget::paintEvent(e); } -void ViewerWidget::compose_sequence(Sequence *s, bool render_audio) { +void ViewerWidget::compose_sequence(Clip* nest, bool render_audio) { + Sequence* s = sequence; + long playhead = panel_timeline->playhead; + if (nest != NULL) { + s = static_cast(nest->media); + +// qDebug() << "nested sequence was null:" << (s == NULL); + + playhead += nest->clip_in - nest->timeline_in; + playhead = refactor_frame_number(playhead, sequence->frame_rate, s->frame_rate); + } + QVector current_clips; for (int i=0;iclip_count();i++) { Clip* c = s->get_clip(i); // if clip starts within one second and/or hasn't finished yet - 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 - if (!c->open) { - open_clip(c, multithreaded); - } + if (c != NULL && !(nest != NULL && c->track != nest->track)) { + bool clip_is_active = false; + switch (c->media_type) { + case 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, playhead)) { + // if thread is already working, we don't want to touch this, + // but we also don't want to hang the UI thread + if (!c->open) { + open_clip(c, multithreaded); + } + + clip_is_active = true; + } else if (c->open) { + close_clip(c); + } + } + break; + case MEDIA_TYPE_SEQUENCE: + clip_is_active = true; + break; + } + + if (clip_is_active) { bool added = false; for (int j=0;jtrack < c->track) { @@ -115,8 +144,6 @@ void ViewerWidget::compose_sequence(Sequence *s, bool render_audio) { if (!added) { current_clips.append(c); } - } else if (c->open) { - close_clip(c); } } } @@ -124,52 +151,38 @@ void ViewerWidget::compose_sequence(Sequence *s, bool render_audio) { for (int i=0;ifinished_opening) { + if (c->media_type == MEDIA_TYPE_FOOTAGE && !c->finished_opening) { qDebug() << "[WARNING] Tried to display clip" << i << "but it's closed"; texture_failed = true; - } else if (is_clip_active(c, panel_timeline->playhead)) { + } else { 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); + get_clip_frame(c, playhead); if (c->texture == NULL) { qDebug() << "[WARNING] Texture hasn't been created yet"; texture_failed = true; - } else if (panel_timeline->playhead >= c->timeline_in) { + } else if (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(); - 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 half_width = sequence->width/2; + int half_height = sequence->height/2; + if (flip) half_height = -half_height; + glOrtho(-half_width, half_width, half_height, -half_height, -1, 1); int anchor_x = ms->video_width/2; int anchor_y = ms->video_height/2; // 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); - } + c->run_video_pre_effect_stack(playhead, &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->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); - } + if (nest != NULL) { + nest->run_video_pre_effect_stack(playhead, &anchor_x, &anchor_y); } int anchor_right = ms->video_width - anchor_x; @@ -199,11 +212,12 @@ void ViewerWidget::compose_sequence(Sequence *s, bool 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); + cache_clip(c, playhead, false, false, c->audio_reset, nest); c->lock.unlock(); } break; case MEDIA_TYPE_SEQUENCE: + compose_sequence(c, render_audio); break; } } @@ -221,7 +235,7 @@ void ViewerWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT); // compose video preview - compose_sequence(sequence, (panel_timeline->playing || force_audio)); + compose_sequence(NULL, (panel_timeline->playing || force_audio)); // send audio to IO device if (panel_timeline->playing) { diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index 8ba9c6a49..7e2ba1da6 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -47,7 +47,7 @@ private: private slots: void retry(); void deleteFunction(); - void compose_sequence(Sequence* s, bool render_audio); + void compose_sequence(Clip *nest, bool render_audio); }; #endif // VIEWERWIDGET_H