From cdbec85a815b5b5963cb789ecf343a5c4c25fdac Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 12 Oct 2018 18:48:14 +1100 Subject: [PATCH] SEVERAL bug fixes --- dialogs/mediapropertiesdialog.cpp | 35 ++++++++++++++ dialogs/mediapropertiesdialog.h | 16 +++++++ dialogs/newsequencedialog.cpp | 39 +++++++++++++--- dialogs/newsequencedialog.h | 5 ++ dialogs/speeddialog.cpp | 3 +- effects/audio/audionoiseeffect.cpp | 8 ++-- effects/audio/paneffect.cpp | 9 ++-- effects/audio/toneeffect.cpp | 8 ++-- effects/audio/volumeeffect.cpp | 6 +-- effects/effect.cpp | 75 +++++++++++++++++++++++------- effects/effect.h | 17 ++++--- io/previewgenerator.cpp | 65 ++++++++++++++++++++++++-- olive.pro | 6 ++- panels/project.cpp | 67 +++++++++++++++++++++++++- panels/project.h | 4 ++ playback/cacher.cpp | 4 +- playback/playback.cpp | 42 +++++++++-------- project/clip.cpp | 48 ++++++++++--------- project/sequence.h | 4 +- project/undo.cpp | 10 ++-- project/undo.h | 1 + ui/keyframeview.cpp | 23 +++++---- ui/labelslider.cpp | 1 + ui/labelslider.h | 2 +- ui/sourcetable.cpp | 11 +++-- ui/sourcetable.h | 2 +- ui/texteditex.cpp | 35 ++++++++------ ui/texteditex.h | 4 ++ ui/timelinewidget.cpp | 2 +- ui/viewerwidget.cpp | 41 ++++++++++------ 30 files changed, 444 insertions(+), 149 deletions(-) create mode 100644 dialogs/mediapropertiesdialog.cpp create mode 100644 dialogs/mediapropertiesdialog.h diff --git a/dialogs/mediapropertiesdialog.cpp b/dialogs/mediapropertiesdialog.cpp new file mode 100644 index 000000000..094ea9019 --- /dev/null +++ b/dialogs/mediapropertiesdialog.cpp @@ -0,0 +1,35 @@ +#include "mediapropertiesdialog.h" + +#include +#include +#include +#include +#include + +#include "io/media.h" +#include "panels/project.h" + +MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent) { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + QGridLayout* grid = new QGridLayout(); + setLayout(grid); + + interlacing_box = new QComboBox(); + interlacing_box->addItem(get_interlacing_name(VIDEO_PROGRESSIVE), VIDEO_PROGRESSIVE); + interlacing_box->addItem(get_interlacing_name(VIDEO_TOP_FIELD_FIRST), VIDEO_TOP_FIELD_FIRST); + interlacing_box->addItem(get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST), VIDEO_BOTTOM_FIELD_FIRST); + + grid->addWidget(new QLabel("Interlacing:"), 0, 0); + grid->addWidget(interlacing_box, 0, 1); + + grid->addWidget(new QLabel("Name:"), 1, 0); + grid->addWidget(new QLineEdit("h"), 1, 1); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + buttons->setCenterButtons(true); + grid->addWidget(buttons, 2, 0, 1, 2); + + connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); +} diff --git a/dialogs/mediapropertiesdialog.h b/dialogs/mediapropertiesdialog.h new file mode 100644 index 000000000..edc29653c --- /dev/null +++ b/dialogs/mediapropertiesdialog.h @@ -0,0 +1,16 @@ +#ifndef MEDIAPROPERTIESDIALOG_H +#define MEDIAPROPERTIESDIALOG_H + +#include + +class QComboBox; + +class MediaPropertiesDialog : public QDialog { + Q_OBJECT +public: + explicit MediaPropertiesDialog(QWidget *parent = 0); +private: + QComboBox* interlacing_box; +}; + +#endif // MEDIAPROPERTIESDIALOG_H diff --git a/dialogs/newsequencedialog.cpp b/dialogs/newsequencedialog.cpp index 3d581f866..fbb0078e7 100644 --- a/dialogs/newsequencedialog.cpp +++ b/dialogs/newsequencedialog.cpp @@ -15,6 +15,7 @@ extern "C" { NewSequenceDialog::NewSequenceDialog(QWidget *parent) : QDialog(parent), + existing_sequence(NULL), ui(new Ui::NewSequenceDialog) { ui->setupUi(this); @@ -51,20 +52,44 @@ void NewSequenceDialog::set_sequence_name(const QString& s) { ui->lineEdit->setText(s); } -void NewSequenceDialog::on_buttonBox_accepted() -{ - Sequence* s = new Sequence(); +void NewSequenceDialog::showEvent(QShowEvent *) { + if (existing_sequence != NULL) { + ui->width_numeric->setValue(existing_sequence->width); + ui->height_numeric->setValue(existing_sequence->height); + int comp_rate = qRound(existing_sequence->frame_rate*100); + for (int i=0;iframe_rate_combobox->count();i++) { + if (qRound(ui->frame_rate_combobox->itemData(i).toDouble()*100) == comp_rate) { + ui->frame_rate_combobox->setCurrentIndex(i); + break; + } + } + ui->lineEdit->setText(existing_sequence->name); + for (int i=0;iaudio_frequency_combobox->count();i++) { + if (ui->audio_frequency_combobox->itemData(i) == existing_sequence->audio_frequency) { + ui->audio_frequency_combobox->setCurrentIndex(i); + break; + } + } + } +} + +void NewSequenceDialog::on_buttonBox_accepted() { + Sequence* s = (existing_sequence != NULL) ? existing_sequence : new Sequence(); s->name = ui->lineEdit->text(); s->width = ui->width_numeric->value(); s->height = ui->height_numeric->value(); - s->frame_rate = ui->frame_rate_combobox->currentData().toDouble(); + s->frame_rate = ui->frame_rate_combobox->currentData().toDouble(); s->audio_frequency = ui->audio_frequency_combobox->currentData().toInt(); s->audio_layout = AV_CH_LAYOUT_STEREO; - ComboAction* ca = new ComboAction(); - panel_project->new_sequence(ca, s, true, NULL); - undo_stack.push(ca); + if (existing_sequence == NULL) { + ComboAction* ca = new ComboAction(); + panel_project->new_sequence(ca, s, true, NULL); + undo_stack.push(ca); + } else { + // TODO make editing undoable + } } void NewSequenceDialog::on_comboBox_currentIndexChanged(int index) diff --git a/dialogs/newsequencedialog.h b/dialogs/newsequencedialog.h index aff4347cd..f676658f2 100644 --- a/dialogs/newsequencedialog.h +++ b/dialogs/newsequencedialog.h @@ -4,6 +4,7 @@ #include class Project; +struct Sequence; namespace Ui { class NewSequenceDialog; @@ -16,8 +17,12 @@ class NewSequenceDialog : public QDialog public: explicit NewSequenceDialog(QWidget *parent = 0); ~NewSequenceDialog(); + Sequence* existing_sequence; void set_sequence_name(const QString& s); +protected: + void showEvent(QShowEvent *); + private slots: void on_buttonBox_accepted(); diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp index 1d4d49a3c..4f48baf2d 100644 --- a/dialogs/speeddialog.cpp +++ b/dialogs/speeddialog.cpp @@ -320,7 +320,8 @@ void set_speed(ComboAction* ca, Clip* c, double speed, bool ripple, long& ep, lo for (int j=0;jrow_count();j++) { EffectRow* r = e->row(j); for (int k=0;kkeyframe_times.size();k++) { - long new_pos = r->keyframe_times.at(k) / speed; + long new_pos = r->keyframe_times.at(k) * c->speed / speed; + qDebug() << "old key" << r->keyframe_times.at(k) << "new key" << new_pos; KeyframeMove* km = new KeyframeMove(); km->movement = new_pos - r->keyframe_times.at(k); km->rows.append(r); diff --git a/effects/audio/audionoiseeffect.cpp b/effects/audio/audionoiseeffect.cpp index add497b68..451f1fcba 100644 --- a/effects/audio/audionoiseeffect.cpp +++ b/effects/audio/audionoiseeffect.cpp @@ -1,6 +1,7 @@ #include "audionoiseeffect.h" #include +#include AudioNoiseEffect::AudioNoiseEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_NOISE_EFFECT) { amount_val = add_row("Amount:")->add_field(EFFECT_FIELD_DOUBLE); @@ -26,11 +27,12 @@ void AudioNoiseEffect::process_audio(double timecode_start, double timecode_end, qint16 right_noise_sample = rand(); // set noise volume - left_noise_sample *= amount_val->get_double_value(timecode)*0.01; - right_noise_sample *= amount_val->get_double_value(timecode)*0.01; + double vol = qSqrt(amount_val->get_double_value(timecode, true)*0.01); + left_noise_sample *= vol; + right_noise_sample *= vol; // mix with source audio - if (mix_val->get_bool_value(timecode)) { + if (mix_val->get_bool_value(timecode, true)) { 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)); left_noise_sample = mix_audio_sample(left_noise_sample, left_sample); diff --git a/effects/audio/paneffect.cpp b/effects/audio/paneffect.cpp index a20593e4c..f9fb692db 100644 --- a/effects/audio/paneffect.cpp +++ b/effects/audio/paneffect.cpp @@ -23,17 +23,16 @@ PanEffect::PanEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_PAN_EFFECT) { void PanEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) { double interval = (timecode_end - timecode_start)/nb_bytes; for (int i=0;iget_double_value(timecode_start+(interval*i)); + double pval = qSqrt(pan_val->get_double_value(timecode_start+(interval*i), true)*0.01); qint16 left_sample = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF)); qint16 right_sample = (qint16) (((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF)); - double val = qPow(pval*0.01, 3); - if (val < 0) { + if (pval < 0) { // affect right channel - right_sample *= (1-std::abs(val)); + right_sample *= (1-std::abs(pval)); } else { // affect left channel - left_sample *= (1-val); + left_sample *= (1-pval); } samples[i+3] = (quint8) (right_sample >> 8); diff --git a/effects/audio/toneeffect.cpp b/effects/audio/toneeffect.cpp index b3b811629..ad3d03ac6 100644 --- a/effects/audio/toneeffect.cpp +++ b/effects/audio/toneeffect.cpp @@ -7,7 +7,7 @@ #include "project/clip.h" #include "project/sequence.h" -ToneEffect::ToneEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_TONE_EFFECT), sinX(0) { +ToneEffect::ToneEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_TONE_EFFECT), sinX(INT_MIN) { type_val = add_row("Type:")->add_field(EFFECT_FIELD_COMBO); type_val->add_combo_item("Sine", TONE_TYPE_SINE); @@ -34,12 +34,12 @@ void ToneEffect::process_audio(double timecode_start, double timecode_end, quint for (int i=0;iget_double_value(timecode))/parent_clip->sequence->audio_frequency)*(amount_val->get_double_value(timecode)*0.01)*INT16_MAX; + qint16 left_tone_sample = qSin((2*M_PI*sinX*freq_val->get_double_value(timecode, true))/parent_clip->sequence->audio_frequency)*qSqrt((amount_val->get_double_value(timecode, true)*0.01))*INT16_MAX; qint16 right_tone_sample = left_tone_sample; // mix with source audio - if (mix_val->get_bool_value(timecode)) { + if (mix_val->get_bool_value(timecode, true)) { 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)); left_tone_sample = mix_audio_sample(left_tone_sample, left_sample); @@ -54,7 +54,7 @@ void ToneEffect::process_audio(double timecode_start, double timecode_end, quint int presin = sinX; sinX++; if (sinX < presin) { - qDebug() << "overflowed"; + qDebug() << "[WARNING] Tone effect overflowed"; } } } diff --git a/effects/audio/volumeeffect.cpp b/effects/audio/volumeeffect.cpp index 10fe1a2d3..51039af86 100644 --- a/effects/audio/volumeeffect.cpp +++ b/effects/audio/volumeeffect.cpp @@ -25,11 +25,9 @@ void VolumeEffect::process_audio(double timecode_start, double timecode_end, qui // qDebug() << timecode_start << timecode_end; double interval = (timecode_end-timecode_start)/nb_bytes; for (int i=0;iget_double_value(timecode_start+(interval*i)); -// qDebug() << timecode_start+(interval*i); + double vol_val = qSqrt(volume_val->get_double_value(timecode_start+(interval*i), true)*0.01); qint32 samp = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF)); - double val = qPow(vol_val*0.01, 3); - samp *= val; + samp *= vol_val; if (samp > INT16_MAX) { samp = INT16_MAX; } else if (samp < INT16_MIN) { diff --git a/effects/effect.cpp b/effects/effect.cpp index 6dde575f6..75bf981c3 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -699,7 +699,7 @@ QVariant EffectField::get_current_data() { switch (type) { case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->value(); break; case EFFECT_FIELD_COLOR: return static_cast(ui_element)->get_color(); break; - case EFFECT_FIELD_STRING: return static_cast(ui_element)->toPlainText(); break; + case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPlainTextEx(); break; case EFFECT_FIELD_BOOL: return static_cast(ui_element)->isChecked(); break; case EFFECT_FIELD_COMBO: return static_cast(ui_element)->currentIndex(); break; case EFFECT_FIELD_FONT: return static_cast(ui_element)->currentText(); break; @@ -765,8 +765,12 @@ void EffectField::get_keyframe_data(double timecode, int &before, int &after, do } } -void EffectField::validate_keyframe_data(double timecode) { - if (parent_row->isKeyframing() && keyframe_data.size() > 0) { +bool EffectField::hasKeyframes() { + return (parent_row->isKeyframing() && keyframe_data.size() > 0); +} + +QVariant EffectField::validate_keyframe_data(double timecode, bool async) { + if (hasKeyframes()) { int before_keyframe; int after_keyframe; double progress; @@ -784,7 +788,10 @@ void EffectField::validate_keyframe_data(double timecode) { double after_dbl = keyframe_data.at(after_keyframe).toDouble(); value = double_lerp(before_dbl, after_dbl, progress); } - static_cast(ui_element)->set_value(value, false); + if (async) { + return value; + } + static_cast(ui_element)->set_value(value, false); } break; case EFFECT_FIELD_COLOR: @@ -797,23 +804,39 @@ void EffectField::validate_keyframe_data(double timecode) { QColor after_data = keyframe_data.at(after_keyframe).value(); value = QColor(lerp(before_data.red(), after_data.red(), progress), lerp(before_data.green(), after_data.green(), progress), lerp(before_data.blue(), after_data.blue(), progress)); } - return static_cast(ui_element)->set_color(value); + if (async) { + return value; + } + static_cast(ui_element)->set_color(value); } break; case EFFECT_FIELD_STRING: + if (async) { + return before_data; + } static_cast(ui_element)->setPlainTextEx(before_data.toString()); break; case EFFECT_FIELD_BOOL: - static_cast(ui_element)->setChecked(before_data.toBool()); + if (async) { + return before_data; + } + static_cast(ui_element)->setChecked(before_data.toBool()); break; case EFFECT_FIELD_COMBO: - static_cast(ui_element)->setCurrentIndexEx(before_data.toInt()); + if (async) { + return before_data; + } + static_cast(ui_element)->setCurrentIndexEx(before_data.toInt()); break; case EFFECT_FIELD_FONT: - static_cast(ui_element)->setCurrentTextEx(before_data.toString()); + if (async) { + return before_data; + } + static_cast(ui_element)->setCurrentTextEx(before_data.toString()); break; } - } + } + return QVariant(); } void EffectField::uiElementChange() { @@ -835,9 +858,12 @@ void EffectField::set_enabled(bool e) { ui_element->setEnabled(e); } -double EffectField::get_double_value(double timecode) { +double EffectField::get_double_value(double timecode, bool async) { + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toDouble(); + } validate_keyframe_data(timecode); - return static_cast(ui_element)->value(); + return static_cast(ui_element)->value(); } void EffectField::set_double_value(double v) { @@ -860,7 +886,10 @@ void EffectField::add_combo_item(const QString& name, const QVariant& data) { static_cast(ui_element)->addItem(name, data); } -int EffectField::get_combo_index(double timecode) { +int EffectField::get_combo_index(double timecode, bool async) { + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toInt(); + } validate_keyframe_data(timecode); return static_cast(ui_element)->currentIndex(); } @@ -883,7 +912,10 @@ void EffectField::set_combo_string(const QString& s) { static_cast(ui_element)->setCurrentTextEx(s); } -bool EffectField::get_bool_value(double timecode) { +bool EffectField::get_bool_value(double timecode, bool async) { + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toBool(); + } validate_keyframe_data(timecode); return static_cast(ui_element)->isChecked(); } @@ -892,16 +924,22 @@ void EffectField::set_bool_value(bool b) { return static_cast(ui_element)->setChecked(b); } -const QString EffectField::get_string_value(double timecode) { +const QString EffectField::get_string_value(double timecode, bool async) { + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toString(); + } validate_keyframe_data(timecode); - return static_cast(ui_element)->toPlainText(); + return static_cast(ui_element)->getPlainTextEx(); } void EffectField::set_string_value(const QString& s) { static_cast(ui_element)->setPlainTextEx(s); } -const QString EffectField::get_font_name(double timecode) { +const QString EffectField::get_font_name(double timecode, bool async) { + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toString(); + } validate_keyframe_data(timecode); return static_cast(ui_element)->currentText(); } @@ -910,7 +948,10 @@ void EffectField::set_font_name(const QString& s) { static_cast(ui_element)->setCurrentText(s); } -QColor EffectField::get_color_value(double timecode) { +QColor EffectField::get_color_value(double timecode, bool async) { + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).value(); + } validate_keyframe_data(timecode); return static_cast(ui_element)->get_color(); } diff --git a/effects/effect.h b/effects/effect.h index 489fa859b..6cc0172f5 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -99,33 +99,31 @@ public: long timecodeToFrame(double timecode); void set_current_data(const QVariant&); void get_keyframe_data(double timecode, int& before, int& after, double& d); - void validate_keyframe_data(double timecode); -// QVariant get_keyframe_data(long p); -// bool is_keyframed(long p); + QVariant validate_keyframe_data(double timecode, bool async = false); - double get_double_value(double timecode); + double get_double_value(double timecode, bool async = false); void set_double_value(double v); void set_double_default_value(double v); void set_double_minimum_value(double v); void set_double_maximum_value(double v); - const QString get_string_value(double timecode); + const QString get_string_value(double timecode, bool async = false); void set_string_value(const QString &s); void add_combo_item(const QString& name, const QVariant &data); - int get_combo_index(double timecode); + int get_combo_index(double timecode, bool async = false); const QVariant get_combo_data(double timecode); const QString get_combo_string(double timecode); void set_combo_index(int index); void set_combo_string(const QString& s); - bool get_bool_value(double timecode); + bool get_bool_value(double timecode, bool async = false); void set_bool_value(bool b); - const QString get_font_name(double timecode); + const QString get_font_name(double timecode, bool async = false); void set_font_name(const QString& s); - QColor get_color_value(double timecode); + QColor get_color_value(double timecode, bool async = false); void set_color_value(QColor color); QWidget* get_ui_element(); @@ -133,6 +131,7 @@ public: QVector keyframe_data; QWidget* ui_element; private: + bool hasKeyframes(); private slots: void uiElementChange(); signals: diff --git a/io/previewgenerator.cpp b/io/previewgenerator.cpp index 443929f6f..1c010d5ad 100644 --- a/io/previewgenerator.cpp +++ b/io/previewgenerator.cpp @@ -2,6 +2,7 @@ #include "media.h" #include "panels/viewer.h" +#include "panels/project.h" #include "io/config.h" #include @@ -301,19 +302,20 @@ void PreviewGenerator::run() { char* filename = new char[ba.size()+1]; strcpy(filename, ba.data()); + QString errorStr; bool error = false; int errCode = avformat_open_input(&fmt_ctx, filename, NULL, NULL); if(errCode != 0) { char err[1024]; av_strerror(errCode, err, 1024); - item->setToolTip(0, "Could not open file - " + QString(err)); + errorStr = "Could not open file - " + QString(err); error = true; } else { errCode = avformat_find_stream_info(fmt_ctx, NULL); if (errCode < 0) { char err[1024]; av_strerror(errCode, err, 1024); - item->setToolTip(0, "Could not find stream information - " + QString(err)); + errorStr = "Could not find stream information - " + QString(err); error = true; } else { av_dump_format(fmt_ctx, 0, filename, 0); @@ -321,12 +323,67 @@ void PreviewGenerator::run() { generate_waveform(); } avformat_close_input(&fmt_ctx); - } + } + QString tooltip = "Name: " + media->name + "\nFilename: " + media->url + "\n"; if (error) { + tooltip += errorStr; emit set_icon(ICON_TYPE_ERROR, replace); } else { - item->setToolTip(0, QString()); +// tooltip += "Video Tracks: " + QString::number(media->video_tracks.size()) + "\nAudio Tracks: " + QString::number(media->audio_tracks.size()) + "\n"; + + if (media->video_tracks.size() > 0) { + tooltip += "Video Dimensions: "; + for (int i=0;ivideo_tracks.size();i++) { + if (i > 0) { + tooltip += ", "; + } + tooltip += QString::number(media->video_tracks.at(i)->video_width) + "x" + QString::number(media->video_tracks.at(i)->video_height); + } + tooltip += "\n"; + + tooltip += "Frame Rate: "; + for (int i=0;ivideo_tracks.size();i++) { + if (i > 0) { + tooltip += ", "; + } + tooltip += QString::number(media->video_tracks.at(i)->video_frame_rate); + if (media->video_tracks.at(i)->video_interlacing != VIDEO_PROGRESSIVE) { + tooltip += " fields (" + QString::number(media->video_tracks.at(i)->video_frame_rate*0.5) + " frames)"; + } + } + tooltip += "\n"; + + tooltip += "Interlacing: "; + for (int i=0;ivideo_tracks.size();i++) { + if (i > 0) { + tooltip += ", "; + } + tooltip += get_interlacing_name(media->video_tracks.at(i)->video_interlacing); + } + tooltip += "\n"; + } + + if (media->audio_tracks.size() > 0) { + tooltip += "Audio Frequency: "; + for (int i=0;iaudio_tracks.size();i++) { + if (i > 0) { + tooltip += ", "; + } + tooltip += QString::number(media->audio_tracks.at(i)->audio_frequency); + } + tooltip += "\n"; + + tooltip += "Audio Channels: "; + for (int i=0;iaudio_tracks.size();i++) { + if (i > 0) { + tooltip += ", "; + } + tooltip += get_channel_layout_name(media->audio_tracks.at(i)->audio_channels, media->audio_tracks.at(i)->audio_layout); + } +// tooltip += "\n"; + } } + item->setToolTip(0, tooltip); delete [] filename; media->preview_gen = NULL; } diff --git a/olive.pro b/olive.pro index 21b0b7225..2cfa28b54 100644 --- a/olive.pro +++ b/olive.pro @@ -81,7 +81,8 @@ SOURCES += \ dialogs/demonotice.cpp \ effects/audio/toneeffect.cpp \ project/marker.cpp \ - dialogs/speeddialog.cpp + dialogs/speeddialog.cpp \ + dialogs/mediapropertiesdialog.cpp HEADERS += \ mainwindow.h \ @@ -141,7 +142,8 @@ HEADERS += \ project/marker.h \ project/selection.h \ dialogs/speeddialog.h \ - dialogs/speeddialog.h + dialogs/speeddialog.h \ + dialogs/mediapropertiesdialog.h FORMS += \ mainwindow.ui \ diff --git a/panels/project.cpp b/panels/project.cpp index a2877a62c..7d0bff7db 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -18,6 +18,8 @@ #include "playback/cacher.h" #include "dialogs/replaceclipmediadialog.h" #include "panels/effectcontrols.h" +#include "dialogs/newsequencedialog.h" +#include "dialogs/mediapropertiesdialog.h" #include #include @@ -28,6 +30,7 @@ #include #include #include +#include #include #include @@ -140,6 +143,39 @@ void Project::replace_clip_media() { } } +void Project::open_properties() { + if (ui->treeWidget->selectedItems().size() == 1) { + QTreeWidgetItem* item = ui->treeWidget->selectedItems().at(0); + switch (get_type_from_tree(item)) { + case MEDIA_TYPE_FOOTAGE: + { + MediaPropertiesDialog mpd(this); + mpd.exec(); + } + break; + case MEDIA_TYPE_SEQUENCE: + { + NewSequenceDialog nsd(this); + Sequence* s = get_sequence_from_tree(item); + nsd.existing_sequence = s; + if (nsd.exec() == QDialog::Accepted) { + set_sequence_of_tree(item, s); + panel_timeline->repaint_timeline(true); + } + } + break; + default: + { + // fall back to renaming + QString new_name = QInputDialog::getText(this, "Rename '" + item->text(0) + "'", "Enter new name:"); + if (!new_name.isEmpty()) { + item->setText(0, new_name); + } + } + } + } +} + void Project::new_sequence(ComboAction *ca, Sequence *s, bool open, QTreeWidgetItem* parent) { QTreeWidgetItem* item = new_item(); item->setText(0, s->name); @@ -521,9 +557,27 @@ Sequence* get_sequence_from_tree(QTreeWidgetItem* item) { return reinterpret_cast(item->data(0, Qt::UserRole + 2).value()); } -void set_sequence_of_tree(QTreeWidgetItem* item, Sequence* sequence) { +QString get_channel_layout_name(int channels, int layout) { + switch (channels) { + case 0: return "Invalid"; break; + case 1: return "Mono"; break; + case 2: return "Stereo"; break; + default: { + char buf[50]; + av_get_channel_layout_string(buf, sizeof(buf), channels, layout); + return QString(buf); + } + } +} + +void set_sequence_of_tree(QTreeWidgetItem* item, Sequence* s) { item->setData(0, Qt::UserRole + 1, MEDIA_TYPE_SEQUENCE); - item->setData(0, Qt::UserRole + 2, QVariant::fromValue(reinterpret_cast(sequence))); + item->setData(0, Qt::UserRole + 2, QVariant::fromValue(reinterpret_cast(s))); + item->setToolTip(0, "Name: " + s->name + + "\nVideo Dimensions: " + QString::number(s->width) + "x" + QString::number(s->height) + + "\nFrame Rate: " + QString::number(s->frame_rate) + + "\nAudio Frequency: " + QString::number(s->audio_frequency) + + "\nAudio Layout: " + get_channel_layout_name(av_get_channel_layout_nb_channels(s->audio_layout), s->audio_layout)); } int get_type_from_tree(QTreeWidgetItem* item) { @@ -1294,3 +1348,12 @@ void MediaThrobber::stop(int icon_type, bool replace) { panel_project->source_table->viewport()->update(); deleteLater(); } + +QString get_interlacing_name(int interlacing) { + switch (interlacing) { + case VIDEO_PROGRESSIVE: return "None (Progressive)"; + case VIDEO_TOP_FIELD_FIRST: return "Top Field First"; + case VIDEO_BOTTOM_FIELD_FIRST: return "Bottom Field First"; + default: return "Invalid"; + } +} diff --git a/panels/project.h b/panels/project.h index efb2129ac..5ae39700e 100644 --- a/panels/project.h +++ b/panels/project.h @@ -36,6 +36,9 @@ Sequence* get_sequence_from_tree(QTreeWidgetItem* item); void set_sequence_of_tree(QTreeWidgetItem* item, Sequence* sequence); void set_item_to_folder(QTreeWidgetItem* item); +QString get_channel_layout_name(int channels, int layout); +QString get_interlacing_name(int interlacing); + class Project : public QDockWidget { Q_OBJECT @@ -70,6 +73,7 @@ public slots: void delete_clips_using_selected_media(); void replace_selected_file(); void replace_clip_media(); + void open_properties(); private: Ui::Project *ui; QTreeWidgetItem* new_item(); diff --git a/playback/cacher.cpp b/playback/cacher.cpp index d8cdefaec..81788c116 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -260,7 +260,7 @@ void cache_audio_worker(Clip* c, Clip* nest) { // apply any audio effects to the data if (nb_bytes == INT_MAX) nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; if (new_frame) { - apply_audio_effects(c, bytes_to_seconds(c->audio_buffer_write, 2, sequence->audio_frequency) + audio_ibuffer_timecode, frame, nb_bytes); + apply_audio_effects(c, bytes_to_seconds(c->audio_buffer_write, 2, sequence->audio_frequency) + audio_ibuffer_timecode + ((double) (c->clip_in - c->timeline_in)/sequence->frame_rate), frame, nb_bytes); } } break; @@ -431,7 +431,7 @@ void reset_cache(Clip* c, long target_frame) { // seeks to nearest keyframe (target_frame represents internal clip frame) int64_t seek_ts = qRound(clip_frame_to_seconds(c, target_frame) / timebase); - av_seek_frame(c->formatCtx, ms->file_index, seek_ts - (av_q2d(av_inv_q(c->stream->time_base))), AVSEEK_FLAG_BACKWARD); + av_seek_frame(c->formatCtx, ms->file_index, seek_ts/* - (av_q2d(av_inv_q(c->stream->time_base)))*/, AVSEEK_FLAG_BACKWARD); // play up to the frame we actually want int ret; diff --git a/playback/playback.cpp b/playback/playback.cpp index 4e874674d..40c91beeb 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -109,7 +109,7 @@ bool get_clip_frame(Clip* c, long playhead) { // do we need to update the texture? MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream); - long sequence_clip_time = playhead - c->timeline_in + c->clip_in; + long sequence_clip_time = qMax(0L, playhead - c->timeline_in + c->clip_in); if (c->reverse && !ms->infinite_length) { sequence_clip_time = c->getMaximumLength() - sequence_clip_time - 1; @@ -117,7 +117,7 @@ bool get_clip_frame(Clip* c, long playhead) { double rate = c->getMediaFrameRate(); if (c->skip_type == SKIP_TYPE_DISCARD) rate *= c->speed; - long clip_time = qMax(0L, refactor_frame_number(sequence_clip_time, c->sequence->frame_rate, rate)); + long clip_time = refactor_frame_number(sequence_clip_time, c->sequence->frame_rate, rate); AVFrame* current_frame = NULL; bool no_frame = false; @@ -206,25 +206,27 @@ bool get_clip_frame(Clip* c, long playhead) { } } - if (current_frame != NULL) { - // set up opengl texture - if (c->texture == NULL) { - c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D); - c->texture->setSize(current_frame->width, current_frame->height); - c->texture->setFormat(QOpenGLTexture::RGBA8_UNorm); - c->texture->setMipLevels(c->texture->maximumMipLevels()); - c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); - c->texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); - } + if (playhead >= c->timeline_in) { + if (current_frame != NULL) { + // set up opengl texture + if (c->texture == NULL) { + c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + c->texture->setSize(current_frame->width, current_frame->height); + c->texture->setFormat(QOpenGLTexture::RGBA8_UNorm); + c->texture->setMipLevels(c->texture->maximumMipLevels()); + c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); + c->texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); + } - glPixelStorei(GL_UNPACK_ROW_LENGTH, current_frame->linesize[0]/4); - c->texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, current_frame->data[0]); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - c->texture_frame = clip_time; - return true; - } else { - texture_failed = true; - qDebug() << "[ERROR] Failed to retrieve frame from cache (R:" << clip_time << "| A:" << c->cache_A.offset << "-" << c->cache_A.offset+c->cache_size-1 << "| B:" << c->cache_B.offset << "-" << c->cache_B.offset+c->cache_size-1 << "| WA:" << c->cache_A.written << "| WB:" << c->cache_B.written << ")"; + glPixelStorei(GL_UNPACK_ROW_LENGTH, current_frame->linesize[0]/4); + c->texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, current_frame->data[0]); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + c->texture_frame = clip_time; + return true; + } else { + texture_failed = true; + qDebug() << "[ERROR] Failed to retrieve frame from cache (R:" << clip_time << "| A:" << c->cache_A.offset << "-" << c->cache_A.offset+c->cache_size-1 << "| B:" << c->cache_B.offset << "-" << c->cache_B.offset+c->cache_size-1 << "| WA:" << c->cache_A.written << "| WB:" << c->cache_B.written << ")"; + } } } return false; diff --git a/project/clip.cpp b/project/clip.cpp index e6346ffe6..0da9553cd 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -178,33 +178,35 @@ long Clip::getLength() { } void Clip::recalculateMaxLength() { - double fr = this->sequence->frame_rate; + if (sequence != NULL) { + double fr = this->sequence->frame_rate; - fr /= speed; + fr /= speed; - switch (media_type) { - case MEDIA_TYPE_FOOTAGE: - { - Media* m = static_cast(media); - MediaStream* ms = m->get_stream_from_file_index(track < 0, media_stream); - if (ms != NULL && ms->infinite_length) { + switch (media_type) { + case MEDIA_TYPE_FOOTAGE: + { + Media* m = static_cast(media); + MediaStream* ms = m->get_stream_from_file_index(track < 0, media_stream); + if (ms != NULL && ms->infinite_length) { + calculated_length = LONG_MAX; + } else { + calculated_length = m->get_length_in_frames(fr); + } + } + break; + case MEDIA_TYPE_SEQUENCE: + { + Sequence* s = static_cast(media); + calculated_length = refactor_frame_number(s->getEndFrame(), s->frame_rate, fr); + } + break; + /*case MEDIA_TYPE_SOLID: + case MEDIA_TYPE_TONE:*/ + default: calculated_length = LONG_MAX; - } else { - calculated_length = m->get_length_in_frames(fr); + break; } - } - break; - case MEDIA_TYPE_SEQUENCE: - { - Sequence* s = static_cast(media); - calculated_length = refactor_frame_number(s->getEndFrame(), s->frame_rate, fr); - } - break; - /*case MEDIA_TYPE_SOLID: - case MEDIA_TYPE_TONE:*/ - default: - calculated_length = LONG_MAX; - break; } } diff --git a/project/sequence.h b/project/sequence.h index ba09f577a..85a57bdc9 100644 --- a/project/sequence.h +++ b/project/sequence.h @@ -8,7 +8,6 @@ #include "project/selection.h" struct Sequence { -public: Sequence(); ~Sequence(); Sequence* copy(); @@ -31,8 +30,7 @@ public: int save_id; QVector markers; - QVector clips; -private: + QVector clips; }; // static variable for the currently active sequence diff --git a/project/undo.cpp b/project/undo.cpp index 742526408..d4230f5d4 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -352,8 +352,10 @@ DeleteMediaCommand::DeleteMediaCommand(QTreeWidgetItem* i) : {} DeleteMediaCommand::~DeleteMediaCommand() { - panel_project->delete_media(item); - delete item; + if (done) { + panel_project->delete_media(item); + delete item; + } } void DeleteMediaCommand::undo() { @@ -364,6 +366,7 @@ void DeleteMediaCommand::undo() { } mainWindow->setWindowModified(old_project_changed); + done = false; } void DeleteMediaCommand::redo() { @@ -373,9 +376,10 @@ void DeleteMediaCommand::redo() { panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(item)); } else { parent->removeChild(item); - } + } mainWindow->setWindowModified(true); + done = true; } RippleCommand::RippleCommand(Sequence *s, long ipoint, long ilength) : diff --git a/project/undo.h b/project/undo.h index 6a4ae18c1..b893d843d 100644 --- a/project/undo.h +++ b/project/undo.h @@ -193,6 +193,7 @@ private: QTreeWidgetItem* item; QTreeWidgetItem* parent; bool old_project_changed; + bool done; }; class RippleCommand : public QUndoCommand { diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 4243a0097..64ce0806a 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -81,6 +81,10 @@ void KeyframeView::paintEvent(QPaintEvent*) { visible_out = effects_out; int max_width = getScreenPointFromFrame(panel_effect_controls->zoom, visible_out - visible_in); + qDebug() << max_width << width(); + if (max_width < width()) { + p.fillRect(QRect(max_width, 0, width(), height()), QColor(0, 0, 0, 64)); + } panel_effect_controls->ui->horizontalScrollBar->setMaximum(qMax(max_width - width(), 0)); header->set_visible_in(effects_in); @@ -152,11 +156,13 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { int row_index = -1; int keyframe_index = -1; long frame_diff = 0; - long frame_min = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()-KEYFRAME_SIZE); - drag_frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()); - long frame_max = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()+KEYFRAME_SIZE); + int mouse_x = event->x() + x_scroll; + int mouse_y = event->y(); + long frame_min = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x-KEYFRAME_SIZE); + drag_frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x); + long frame_max = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x+KEYFRAME_SIZE); for (int i=0;iy() > rowY.at(i)-KEYFRAME_SIZE-KEYFRAME_SIZE && event->y() < rowY.at(i)+KEYFRAME_SIZE+KEYFRAME_SIZE) { + if (mouse_y > rowY.at(i)-KEYFRAME_SIZE-KEYFRAME_SIZE && mouse_y < rowY.at(i)+KEYFRAME_SIZE+KEYFRAME_SIZE) { EffectRow* row = rows.at(i); for (int j=0;jkeyframe_times.size();j++) { long eval_keyframe_time = row->keyframe_times.at(j)-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); @@ -199,9 +205,10 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { void KeyframeView::mouseMoveEvent(QMouseEvent* event) { if (mousedown) { + int mouse_x = event->x() + x_scroll; if (keys_selected) { // move keyframes - frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()) - drag_frame_start; + frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x) - drag_frame_start; // snapping to playhead if (panel_timeline->snapping) { @@ -210,7 +217,7 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { Clip* c = row->parent_effect->parent_clip; long key_time = row->keyframe_times.at(selected_keyframes.at(i)) + frame_diff - c->clip_in + c->timeline_in; long key_eval = key_time; - if (panel_timeline->snap_to_timeline(&key_eval, true, true, true)) { + if (panel_timeline->snap_to_point(sequence->playhead, &key_eval)) { frame_diff += (key_eval - key_time); break; } @@ -245,8 +252,8 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { int min_row = qMin(rect_select_y, event->y())-KEYFRAME_SIZE; int max_row = qMax(rect_select_y, event->y())+KEYFRAME_SIZE; - long frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, rect_select_x); - long frame_end = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()); + long frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, rect_select_x+x_scroll); + long frame_end = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x); long min_frame = qMin(frame_start, frame_end)-KEYFRAME_SIZE; long max_frame = qMax(frame_start, frame_end)+KEYFRAME_SIZE; diff --git a/ui/labelslider.cpp b/ui/labelslider.cpp index aed972f2f..fa7d6de6f 100644 --- a/ui/labelslider.cpp +++ b/ui/labelslider.cpp @@ -8,6 +8,7 @@ #include #include #include +#include LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) { decimal_places = 1; diff --git a/ui/labelslider.h b/ui/labelslider.h index 45fdd13a5..a25e5d1ad 100644 --- a/ui/labelslider.h +++ b/ui/labelslider.h @@ -22,7 +22,7 @@ public: bool is_set(); double get_drag_start_value(); bool is_dragging(); - virtual QString valueToString(double v); + QString valueToString(double v); double getPreviousValue(); int decimal_places; protected: diff --git a/ui/sourcetable.cpp b/ui/sourcetable.cpp index 59ea66d7c..6d18b1ca0 100644 --- a/ui/sourcetable.cpp +++ b/ui/sourcetable.cpp @@ -24,10 +24,10 @@ SourceTable::SourceTable(QWidget* parent) : QTreeWidget(parent) { connect(&rename_timer, SIGNAL(timeout()), this, SLOT(rename_interval())); connect(this, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(item_click(QTreeWidgetItem*,int))); connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(item_renamed(QTreeWidgetItem*))); - connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu())); } -void SourceTable::show_context_menu(const QPoint& pos) { +void SourceTable::show_context_menu() { QMenu menu(this); if (selectedItems().size() == 0) { @@ -75,9 +75,14 @@ void SourceTable::show_context_menu(const QPoint& pos) { // delete media QAction* delete_action = menu.addAction("Delete"); connect(delete_action, SIGNAL(triggered(bool)), panel_project, SLOT(delete_selected_media())); + + if (selectedItems().size() == 1) { + QAction* properties_action = menu.addAction("Properties..."); + connect(properties_action, SIGNAL(triggered(bool)), panel_project, SLOT(open_properties())); + } } - menu.exec(mapToGlobal(pos)); + menu.exec(QCursor::pos()); } void SourceTable::item_renamed(QTreeWidgetItem* item) { diff --git a/ui/sourcetable.h b/ui/sourcetable.h index c21b6ac0f..4236e77fc 100644 --- a/ui/sourcetable.h +++ b/ui/sourcetable.h @@ -27,7 +27,7 @@ private slots: void item_click(QTreeWidgetItem* item, int column); void stop_rename_timer(); void item_renamed(QTreeWidgetItem *item); - void show_context_menu(const QPoint& pos); + void show_context_menu(); }; #endif // SOURCETABLE_H diff --git a/ui/texteditex.cpp b/ui/texteditex.cpp index 5b8d71fe7..7c29db865 100644 --- a/ui/texteditex.cpp +++ b/ui/texteditex.cpp @@ -5,22 +5,17 @@ TextEditEx::TextEditEx(QWidget *parent) : QTextEdit(parent) { setUndoRedoEnabled(false); connect(this, SIGNAL(textChanged()), this, SLOT(updateInternals())); + connect(this, SIGNAL(updateSelf()), this, SLOT(updateText())); } -void TextEditEx::setPlainTextEx(const QString &text) { - blockSignals(true); +const QString& TextEditEx::getPlainTextEx() { + return text; +} - int pos = textCursor().position(); - - setPlainText(text); - - QTextCursor newCursor(document()); - newCursor.setPosition(pos); - setTextCursor(newCursor); - - updateInternals(); - - blockSignals(false); +void TextEditEx::setPlainTextEx(const QString &t) { + previousText = text; + text = t; + emit updateSelf(); } const QString &TextEditEx::getPreviousValue() { @@ -31,3 +26,17 @@ void TextEditEx::updateInternals() { previousText = text; text = toPlainText(); } + +void TextEditEx::updateText() { + blockSignals(true); + + int pos = textCursor().position(); + + setPlainText(text); + + QTextCursor newCursor(document()); + newCursor.setPosition(pos); + setTextCursor(newCursor); + + blockSignals(false); +} diff --git a/ui/texteditex.h b/ui/texteditex.h index ebe3f49e9..564da3723 100644 --- a/ui/texteditex.h +++ b/ui/texteditex.h @@ -9,8 +9,12 @@ public: TextEditEx(QWidget* parent = 0); void setPlainTextEx(const QString &text); const QString& getPreviousValue(); + const QString& getPlainTextEx(); +signals: + void updateSelf(); private slots: void updateInternals(); + void updateText(); private: QString previousText; QString text; diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index b0bc773bb..ae996dfcf 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -1947,7 +1947,7 @@ void TimelineWidget::paintEvent(QPaintEvent*) { for (int i=0;i<2;i++) { Transition* t = (i == 0) ? clip->opening_transition : clip->closing_transition; if (t != NULL) { - int transition_width = panel_timeline->getTimelineScreenPointFromFrame(t->length); + int transition_width = getScreenPointFromFrame(panel_timeline->zoom, t->length); int transition_height = clip_rect.height(); int tr_y = clip_rect.y(); int tr_x = 0; diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 021805ec7..77242c0e8 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -85,7 +85,7 @@ void ViewerWidget::drawTitleSafeArea() { glLoadIdentity(); glOrtho(-halfWidth, halfWidth, halfHeight, -halfHeight, -1, 1); - glColor4f(0.5, 0.5, 0.5, 1.0); + glColor4f(0.66f, 0.66f, 0.66f, 1.0f); glBegin(GL_LINES); // action safe rectangle @@ -378,6 +378,8 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) { glVertex2f(coords.vertexBottomLeftX, coords.vertexBottomLeftY); // bottom left glEnd(); + glBindTexture(GL_TEXTURE_2D, 0); + if (nest != NULL) { nest->fbo[0]->release(); if (default_fbo != NULL) default_fbo->bind(); @@ -386,19 +388,32 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) { glPopMatrix(); } else { - switch (c->media_type) { - case MEDIA_TYPE_FOOTAGE: - case MEDIA_TYPE_TONE: - if (render_audio - && c->lock.tryLock()) { - // clip is not caching, start caching audio - cache_clip(c, playhead, false, false, c->audio_reset, nest); - c->lock.unlock(); + if (render_audio) { + switch (c->media_type) { + case MEDIA_TYPE_FOOTAGE: + case MEDIA_TYPE_TONE: + if (c->lock.tryLock()) { + // clip is not caching, start caching 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; + } + } + + // visually update all the keyframe values + double ts = (playhead - c->timeline_in + c->clip_in)/sequence->frame_rate; + for (int i=0;ieffects.size();i++) { + Effect* e = c->effects.at(i); + for (int j=0;jrow_count();j++) { + EffectRow* r = e->row(j); + for (int k=0;kfieldCount();k++) { + r->field(k)->validate_keyframe_data(ts); + } } - break; - case MEDIA_TYPE_SEQUENCE: - compose_sequence(c, render_audio); - break; } } }