Merge pull request #189 from olive-editor/alpha

Merge current alpha to master
This commit is contained in:
itsmattkc
2018-12-23 11:58:18 +11:00
committed by GitHub
86 changed files with 4150 additions and 2831 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ void setup_debug() {
debug_file.setFileName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/debug_log");
if (debug_file.open(QFile::WriteOnly)) {
QString debug_intro = "Olive Session " + QString::number(QDateTime::currentMSecsSinceEpoch());
debug_file.write(debug_intro.toLatin1());
debug_file.write(debug_intro.toUtf8());
} else {
debug_out = QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug();
}
+16 -7
View File
@@ -6,6 +6,7 @@
#include <QThread>
#include <QMessageBox>
#include <QOpenGLContext>
#include <QtMath>
#include "debug.h"
#include "panels/panels.h"
@@ -312,8 +313,7 @@ void ExportDialog::on_formatCombobox_currentIndexChanged(int index)
ui->audioGroupbox->setEnabled(audio_enabled);
}
void ExportDialog::on_pushButton_2_clicked()
{
void ExportDialog::on_pushButton_2_clicked() {
close();
}
@@ -324,6 +324,8 @@ void ExportDialog::render_thread_finished() {
prep_ui_for_render(false);
panel_sequence_viewer->viewer_widget->makeCurrent();
panel_sequence_viewer->viewer_widget->initializeGL();
update_ui(false);
if (ui->progressBar->value() == 100) close();
}
void ExportDialog::prep_ui_for_render(bool r) {
@@ -474,7 +476,7 @@ void ExportDialog::on_pushButton_clicked() {
connect(et, SIGNAL(finished()), et, SLOT(deleteLater()));
connect(et, SIGNAL(finished()), this, SLOT(render_thread_finished()));
connect(et, SIGNAL(progress_changed(int)), this, SLOT(update_progress_bar(int)));
connect(et, SIGNAL(progress_changed(int, qint64)), this, SLOT(update_progress_bar(int, qint64)));
closeActiveClips(sequence, true);
@@ -517,11 +519,18 @@ void ExportDialog::on_pushButton_clicked() {
}
}
void ExportDialog::update_progress_bar(int value) {
ui->progressBar->setValue(value);
void ExportDialog::update_progress_bar(int value, qint64 remaining_ms) {
// convert ms to H:MM:SS
int seconds = qFloor(remaining_ms*0.001)%60;
int minutes = qFloor(remaining_ms/60000)%60;
int hours = qFloor(remaining_ms/3600000);
ui->progressBar->setFormat("%p% (ETA: " + QString::number(hours) + ":" + QString::number(minutes).rightJustified(2, '0') + ":" + QString::number(seconds).rightJustified(2, '0') + ")");
ui->progressBar->setValue(value);
}
void ExportDialog::on_renderCancel_clicked() {
panel_sequence_viewer->viewer_widget->force_quit = true;
et->continueEncode = false;
cancelled = true;
}
@@ -552,9 +561,9 @@ void ExportDialog::on_compressionTypeCombobox_currentIndexChanged(int) {
break;
case COMPRESSION_TYPE_CFR:
ui->videoBitrateLabel->setText("Quality (CRF):");
ui->videobitrateSpinbox->setValue(23);
ui->videobitrateSpinbox->setValue(36);
ui->videobitrateSpinbox->setMaximum(51);
ui->videobitrateSpinbox->setToolTip("Quality Factor:\n\n0 = lossless\n17-18 = visually lossless (compressed, but unnoticeable)\n23 = default, high quality\n51 = lowest quality possible");
ui->videobitrateSpinbox->setToolTip("Quality Factor:\n\n0 = lossless\n17-18 = visually lossless (compressed, but unnoticeable)\n23 = high quality\n51 = lowest quality possible");
break;
case COMPRESSION_TYPE_TARGETSIZE:
ui->videoBitrateLabel->setText("Target File Size (MB):");
+1 -1
View File
@@ -25,7 +25,7 @@ private slots:
void on_pushButton_clicked();
void update_progress_bar(int value);
void update_progress_bar(int value, qint64 remaining_ms);
void on_renderCancel_clicked();
+5 -2
View File
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>358</width>
<height>470</height>
<width>443</width>
<height>562</height>
</rect>
</property>
<property name="windowTitle">
@@ -227,6 +227,9 @@
<property name="value">
<number>0</number>
</property>
<property name="format">
<string>%p% (ETA: 0:00:00)</string>
</property>
</widget>
</item>
<item>
+36 -6
View File
@@ -6,25 +6,55 @@
#include <QProgressBar>
#include <QPushButton>
LoadDialog::LoadDialog(QWidget *parent) : QDialog(parent) {
#include "panels/panels.h"
#include "panels/project.h"
#include "io/loadthread.h"
#include "playback/playback.h"
#include "ui/sourcetable.h"
#include "mainwindow.h"
LoadDialog::LoadDialog(QWidget *parent, bool autorecovery) : QDialog(parent) {
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QVBoxLayout* layout = new QVBoxLayout();
setLayout(layout);
layout->addWidget(new QLabel("Loading ''..."));
layout->addWidget(new QLabel("Loading '" + project_url.mid(project_url.lastIndexOf('/')+1) + "'..."));
bar = new QProgressBar();
bar->setValue(50);
bar->setValue(0);
layout->addWidget(bar);
QPushButton* cancel_button = new QPushButton("Cancel");
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(reject()));
cancel_button = new QPushButton("Cancel");
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(cancel()));
QHBoxLayout* hboxLayout = new QHBoxLayout();
hboxLayout = new QHBoxLayout();
hboxLayout->addStretch();
hboxLayout->addWidget(cancel_button);
hboxLayout->addStretch();
layout->addLayout(hboxLayout);
update();
lt = new LoadThread(this, autorecovery);
QObject::connect(lt, SIGNAL(success()), this, SLOT(thread_done()));
QObject::connect(lt, SIGNAL(error()), this, SLOT(die()));
QObject::connect(lt, SIGNAL(report_progress(int)), bar, SLOT(setValue(int)));
lt->start();
}
void LoadDialog::cancel() {
lt->cancel();
lt->wait();
die();
}
void LoadDialog::die() {
mainWindow->new_project();
reject();
}
void LoadDialog::thread_done() {
accept();
}
+14 -1
View File
@@ -4,13 +4,26 @@
#include <QDialog>
class QProgressBar;
struct Sequence;
class Media;
struct Footage;
class QHBoxLayout;
class LoadThread;
class LoadDialog : public QDialog
{
Q_OBJECT
public:
LoadDialog(QWidget* parent = 0);
LoadDialog(QWidget* parent, bool autorecovery);
private slots:
void cancel();
void die();
void thread_done();
private:
QProgressBar* bar;
QPushButton* cancel_button;
QHBoxLayout* hboxLayout;
LoadThread* lt;
};
#endif // LOADDIALOG_H
+14 -16
View File
@@ -7,34 +7,35 @@
#include <QDialogButtonBox>
#include <QTreeWidgetItem>
#include "io/media.h"
#include "project/footage.h"
#include "project/media.h"
#include "panels/project.h"
#include "project/undo.h"
MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, QTreeWidgetItem *i, Media *m) :
MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) :
QDialog(parent),
item(i),
media(m)
item(i)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QGridLayout* grid = new QGridLayout();
setLayout(grid);
if (m->video_tracks.size() > 0) {
Footage* f = item->to_footage();
if (f->video_tracks.size() > 0) {
interlacing_box = new QComboBox();
interlacing_box->addItem("Auto (" + get_interlacing_name(media->video_tracks.at(0)->video_auto_interlacing) + ")");
interlacing_box->addItem("Auto (" + get_interlacing_name(f->video_tracks.at(0)->video_auto_interlacing) + ")");
interlacing_box->addItem(get_interlacing_name(VIDEO_PROGRESSIVE));
interlacing_box->addItem(get_interlacing_name(VIDEO_TOP_FIELD_FIRST));
interlacing_box->addItem(get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST));
interlacing_box->setCurrentIndex((media->video_tracks.at(0)->video_auto_interlacing == media->video_tracks.at(0)->video_interlacing) ? 0 : media->video_tracks.at(0)->video_interlacing + 1);
interlacing_box->setCurrentIndex((f->video_tracks.at(0)->video_auto_interlacing == f->video_tracks.at(0)->video_interlacing) ? 0 : f->video_tracks.at(0)->video_interlacing + 1);
grid->addWidget(new QLabel("Interlacing:"), 0, 0);
grid->addWidget(interlacing_box, 0, 1);
}
name_box = new QLineEdit(m->name);
name_box = new QLineEdit(item->get_name());
grid->addWidget(new QLabel("Name:"), 1, 0);
grid->addWidget(name_box, 1, 1);
@@ -50,22 +51,19 @@ void MediaPropertiesDialog::accept() {
ComboAction* ca = new ComboAction();
//set interlacing
Footage* f = item->to_footage();
if (interlacing_box->currentIndex() > 0) {
ca->append(new SetInt(&media->video_tracks.at(0)->video_interlacing, interlacing_box->currentIndex() - 1));
ca->append(new SetInt(&f->video_tracks.at(0)->video_interlacing, interlacing_box->currentIndex() - 1));
} else {
ca->append(new SetInt(&media->video_tracks.at(0)->video_interlacing, media->video_tracks.at(0)->video_auto_interlacing));
ca->append(new SetInt(&f->video_tracks.at(0)->video_interlacing, f->video_tracks.at(0)->video_auto_interlacing));
}
//set name
MediaRename* mr = new MediaRename();
mr->from = media->name;
mr->item = item;
mr->to = name_box->text();
item->setText(0, name_box->text());
MediaRename* mr = new MediaRename(item, name_box->text());
ca->append(mr);
ca->appendPost(new CloseAllClipsCommand());
ca->appendPost(new UpdateFootageTooltip(item, media));
ca->appendPost(new UpdateFootageTooltip(item));
undo_stack.push(ca);
+4 -5
View File
@@ -3,20 +3,19 @@
#include <QDialog>
struct Media;
struct Footage;
class QComboBox;
class QLineEdit;
class QTreeWidgetItem;
class Media;
class MediaPropertiesDialog : public QDialog {
Q_OBJECT
public:
MediaPropertiesDialog(QWidget *parent, QTreeWidgetItem* i, Media *m);
MediaPropertiesDialog(QWidget *parent, Media* i);
private:
QComboBox* interlacing_box;
QLineEdit* name_box;
QTreeWidgetItem* item;
Media* media;
Media* item;
private slots:
void accept();
};
+2 -2
View File
@@ -4,7 +4,7 @@
#include <QDialog>
class Project;
class QTreeWidgetItem;
class Media;
struct Sequence;
namespace Ui {
@@ -19,7 +19,7 @@ public:
explicit NewSequenceDialog(QWidget *parent = 0);
~NewSequenceDialog();
Sequence* existing_sequence;
QTreeWidgetItem* existing_item;
Media* existing_item;
void set_sequence_name(const QString& s);
protected:
+3
View File
@@ -22,6 +22,8 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) :
{
ui->setupUi(this);
ui->accurateSeekButton->setChecked(!config.fast_seeking);
ui->fastSeekButton->setChecked(config.fast_seeking);
ui->recordingComboBox->setCurrentIndex(config.recording_mode - 1);
ui->imgSeqFormatEdit->setText(config.img_seq_formats);
}
@@ -77,4 +79,5 @@ void PreferencesDialog::setup_kbd_shortcuts(QMenuBar* menubar) {
void PreferencesDialog::on_buttonBox_accepted() {
config.recording_mode = ui->recordingComboBox->currentIndex() + 1;
config.img_seq_formats = ui->imgSeqFormatEdit->text();
config.fast_seeking = ui->fastSeekButton->isChecked();
}
+33 -1
View File
@@ -17,7 +17,7 @@
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
@@ -62,6 +62,38 @@
<string>Behavior</string>
</attribute>
</widget>
<widget class="QWidget" name="tab_4">
<attribute name="title">
<string>Playback</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Seeking</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QRadioButton" name="accurateSeekButton">
<property name="text">
<string>Accurate Seeking
Always show the correct frame (visual may pause briefly as correct frame is retrieved)</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="fastSeekButton">
<property name="text">
<string>Fast Seeking
Seek quickly (may briefly show inaccurate frames when seeking - doesn't affect playback/export)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Keyboard</string>
+18 -51
View File
@@ -8,17 +8,18 @@
#include "project/clip.h"
#include "playback/playback.h"
#include "playback/cacher.h"
#include "io/media.h"
#include "project/footage.h"
#include "project/undo.h"
#include "project/media.h"
#include <QVBoxLayout>
#include <QTreeWidget>
#include <QTreeView>
#include <QLabel>
#include <QPushButton>
#include <QMessageBox>
#include <QCheckBox>
ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, SourceTable *table, QTreeWidgetItem *old_media) :
ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, SourceTable *table, Media *old_media) :
QDialog(parent),
source_table(table),
media(old_media)
@@ -29,12 +30,11 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, SourceTable *tab
layout->addWidget(new QLabel("Select which media you want to replace this media's clips with:"));
tree = new QTreeWidget();
tree->setHeaderHidden(true);
tree = new QTreeView();
layout->addWidget(tree);
use_same_media_in_points = new QCheckBox("Keep the same media in points");
use_same_media_in_points = new QCheckBox("Keep the same media in-points");
use_same_media_in_points->setChecked(true);
layout->addWidget(use_same_media_in_points);
@@ -56,36 +56,34 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, SourceTable *tab
setLayout(layout);
copy_tree(NULL, NULL);
tree->setModel(&project_model);
//copy_tree(NULL, NULL);
}
void ReplaceClipMediaDialog::replace() {
if (tree->selectedItems().size() != 1) {
QModelIndexList selected_items = tree->selectionModel()->selectedRows();
if (selected_items.size() != 1) {
QMessageBox::critical(this, "No media selected", "Please select a media to replace with or click 'Cancel'.", QMessageBox::Ok);
} else {
QTreeWidgetItem* selected_item = tree->selectedItems().at(0);
QTreeWidgetItem* new_item = reinterpret_cast<QTreeWidgetItem*>(selected_item->data(0, Qt::UserRole + 1).value<quintptr>());
Media* new_item = static_cast<Media*>(selected_items.at(0).internalPointer());
if (media == new_item) {
QMessageBox::critical(this, "Same media selected", "You selected the same media that you're replacing. Please select a different one or click 'Cancel'.", QMessageBox::Ok);
} else if (get_type_from_tree(new_item) == MEDIA_TYPE_FOLDER) {
} else if (new_item->get_type() == MEDIA_TYPE_FOLDER) {
QMessageBox::critical(this, "Folder selected", "You cannot replace footage with a folder.", QMessageBox::Ok);
} else {
if (get_type_from_tree(new_item) == MEDIA_TYPE_SEQUENCE && sequence == get_sequence_from_tree(new_item)) {
if (new_item->get_type() == MEDIA_TYPE_SEQUENCE && sequence == new_item->to_sequence()) {
QMessageBox::critical(this, "Active sequence selected", "You cannot insert a sequence into itself.", QMessageBox::Ok);
} else {
void* old_media = get_media_from_tree(media);
} else {
ReplaceClipMediaCommand* rcmc = new ReplaceClipMediaCommand(
old_media,
get_media_from_tree(new_item),
get_type_from_tree(media),
get_type_from_tree(new_item),
media,
new_item,
use_same_media_in_points->isChecked()
);
for (int i=0;i<sequence->clips.size();i++) {
Clip* c = sequence->clips.at(i);
if (c != NULL && c->media == old_media) {
if (c != NULL && c->media == media) {
rcmc->clips.append(c);
}
}
@@ -98,34 +96,3 @@ void ReplaceClipMediaDialog::replace() {
}
}
}
void ReplaceClipMediaDialog::copy_tree(QTreeWidgetItem* parent, QTreeWidgetItem* target) {
QVector<QTreeWidgetItem*> items;
if (parent == NULL) {
for (int i=0;i<source_table->topLevelItemCount();i++) {
items.append(source_table->topLevelItem(i));
}
} else {
for (int i=0;i<parent->childCount();i++) {
items.append(parent->child(i));
}
}
for (int i=0;i<items.size();i++) {
QTreeWidgetItem* original = items.at(i);
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText(0, original->text(0));
item->setData(0, Qt::UserRole + 1, reinterpret_cast<quintptr>(original));
if (target == NULL) {
tree->addTopLevelItem(item);
} else {
target->addChild(item);
}
if (original->childCount() > 0) {
copy_tree(original, item);
}
}
}
+6 -7
View File
@@ -4,22 +4,21 @@
#include <QDialog>
class SourceTable;
class QTreeWidget;
class QTreeWidgetItem;
class QTreeView;
class Media;
class QCheckBox;
class ReplaceClipMediaDialog : public QDialog {
Q_OBJECT
public:
ReplaceClipMediaDialog(QWidget* parent, SourceTable* table, QTreeWidgetItem *old_media);
ReplaceClipMediaDialog(QWidget* parent, SourceTable* table, Media *old_media);
private slots:
void replace();
private:
SourceTable* source_table;
QTreeWidget* tree;
QTreeWidgetItem* media;
QCheckBox* use_same_media_in_points;
void copy_tree(QTreeWidgetItem* parent, QTreeWidgetItem *target);
QTreeView* tree;
Media* media;
QCheckBox* use_same_media_in_points;
};
#endif // REPLACECLIPMEDIADIALOG_H
+8 -9
View File
@@ -10,12 +10,13 @@
#include "ui/labelslider.h"
#include "project/clip.h"
#include "project/sequence.h"
#include "io/media.h"
#include "project/footage.h"
#include "playback/playback.h"
#include "panels/panels.h"
#include "panels/timeline.h"
#include "project/undo.h"
#include "project/effect.h"
#include "project/media.h"
SpeedDialog::SpeedDialog(QWidget *parent) : QDialog(parent) {
QVBoxLayout* main_layout = new QVBoxLayout();
@@ -83,14 +84,12 @@ void SpeedDialog::run() {
clip_percent = c->speed;
if (c->track < 0) {
bool process_video = true;
if (c->media_type == MEDIA_TYPE_FOOTAGE) {
Media* m = static_cast<Media*>(c->media);
if (m != NULL) {
MediaStream* ms = m->get_stream_from_file_index(true, c->media_stream);
if (ms != NULL && ms->infinite_length) {
process_video = false;
}
}
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
Footage* m = c->media->to_footage();
FootageStream* ms = m->get_stream_from_file_index(true, c->media_stream);
if (ms != NULL && ms->infinite_length) {
process_video = false;
}
}
if (process_video) {
+103
View File
@@ -0,0 +1,103 @@
#include "stabilizerdialog.h"
#include <QVBoxLayout>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QGroupBox>
#include <QLabel>
#include "ui/labelslider.h"
StabilizerDialog::StabilizerDialog(QWidget *parent) : QDialog(parent) {
setWindowTitle("Stabilizer");
layout = new QVBoxLayout(this);
setLayout(layout);
enable_stab = new QCheckBox(this);
enable_stab->setText("Enable Stabilizer");
layout->addWidget(enable_stab);
analysis = new QGroupBox("Analysis", this);
layout->addWidget(analysis);
analysis_layout = new QGridLayout(analysis);
analysis->setLayout(analysis_layout);
analysis_layout->addWidget(new QLabel("Shakiness:"), 0, 0);
shakiness_slider = new LabelSlider();
shakiness_slider->set_minimum_value(1);
shakiness_slider->set_default_value(5);
shakiness_slider->set_maximum_value(10);
analysis_layout->addWidget(shakiness_slider, 0, 1);
analysis_layout->addWidget(new QLabel("Accuracy:"), 1, 0);
accuracy_slider = new LabelSlider();
accuracy_slider->set_minimum_value(1);
accuracy_slider->set_default_value(15);
accuracy_slider->set_maximum_value(15);
analysis_layout->addWidget(accuracy_slider, 1, 1);
analysis_layout->addWidget(new QLabel("Step Size:"), 2, 0);
stepsize_slider = new LabelSlider();
stepsize_slider->set_minimum_value(1);
stepsize_slider->set_default_value(6);
analysis_layout->addWidget(stepsize_slider, 2, 1);
analysis_layout->addWidget(new QLabel("Minimum Contrast:"), 3, 0);
mincontrast_slider = new LabelSlider();
mincontrast_slider->set_minimum_value(0);
mincontrast_slider->set_default_value(0.3);
mincontrast_slider->set_maximum_value(1);
analysis_layout->addWidget(mincontrast_slider, 3, 1);
/*analysis_layout->addWidget(new QLabel("Tripod Mode:"), 4, 0);
tripod_mode_box = new QCheckBox();
analysis_layout->addWidget(tripod_mode_box, 4, 1);*/
stabilization = new QGroupBox("Stabilization", this);
layout->addWidget(stabilization);
stabilization_layout = new QGridLayout();
stabilization->setLayout(stabilization_layout);
stabilization_layout->addWidget(new QLabel("Smoothing:"), 0, 0);
smoothing_slider = new LabelSlider();
smoothing_slider->set_minimum_value(0);
smoothing_slider->set_default_value(10);
stabilization_layout->addWidget(smoothing_slider, 0, 1);
stabilization_layout->addWidget(new QLabel("Gaussian Motion:"), 1, 0);
gaussian_motion = new QCheckBox();
gaussian_motion->setChecked(true);
stabilization_layout->addWidget(gaussian_motion, 1, 1);
stabilization_layout->addWidget(new QLabel("Maximum Movement:"), 2, 0);
stabilization_layout->addWidget(new QLabel("Maximum Rotation:"), 3, 0);
stabilization_layout->addWidget(new QLabel("Crop:"), 4, 0);
stabilization_layout->addWidget(new QLabel("Zoom Behavior:"), 5, 0);
stabilization_layout->addWidget(new QLabel("Zoom Speed:"), 6, 0);
stabilization_layout->addWidget(new QLabel("Interpolation Quality:"), 7, 0);
buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
layout->addWidget(buttons);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
connect(enable_stab, SIGNAL(toggled(bool)), this, SLOT(set_all_enabled(bool)));
set_all_enabled(false);
}
void StabilizerDialog::set_all_enabled(bool e) {
analysis->setEnabled(e);
stabilization->setEnabled(e);
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef STABILIZERDIALOG_H
#define STABILIZERDIALOG_H
class QVBoxLayout;
class QCheckBox;
class QDialogButtonBox;
class QGroupBox;
class QGridLayout;
class LabelSlider;
#include <QDialog>
class StabilizerDialog : public QDialog
{
Q_OBJECT
public:
StabilizerDialog(QWidget* parent = 0);
private slots:
void set_all_enabled(bool e);
private:
QVBoxLayout* layout;
QCheckBox* enable_stab;
QDialogButtonBox* buttons;
QGroupBox* analysis;
QGridLayout* analysis_layout;
LabelSlider* shakiness_slider;
LabelSlider* accuracy_slider;
LabelSlider* stepsize_slider;
LabelSlider* mincontrast_slider;
QCheckBox* tripod_mode_box;
QGroupBox* stabilization;
QGridLayout* stabilization_layout;
LabelSlider* smoothing_slider;
QCheckBox* gaussian_motion;
};
#endif // STABILIZERDIALOG_H
+4 -2
View File
@@ -8,7 +8,7 @@ uniform bool horiz_blur;
uniform bool vert_blur;
void main(void) {
float rad = floor(radius);
float rad = ceil(radius);
float x_rad = (horiz_blur) ? rad : 0.5;
float y_rad = (vert_blur) ? rad : 0.5;
vec2 texCoord = gl_FragCoord.xy/resolution;
@@ -18,10 +18,12 @@ void main(void) {
float divider = 1.0;
if (horiz_blur) divider /= rad;
if (vert_blur) divider /= rad;
vec4 color = vec4(0.0);
for (float x=-x_rad+0.5;x<=x_rad;x+=2.0) {
for (float y=-y_rad+0.5;y<=y_rad;y+=2.0) {
gl_FragColor += texture2D(image, (vec2(gl_FragCoord.x+x, gl_FragCoord.y+y))/resolution)*(divider);
color += texture2D(image, (vec2(gl_FragCoord.x+x, gl_FragCoord.y+y))/resolution)*(divider);
}
}
gl_FragColor = color;
}
}
+6 -3
View File
@@ -11,16 +11,19 @@ uniform vec2 resolution;
void main(void) {
if (length > 0.0) {
float ceillen = ceil(length);
float radians = (angle*M_PI)/180.0;
float divider = 1.0 / length;
float divider = 1.0 / ceillen;
float sin_angle = sin(radians);
float cos_angle = cos(radians);
for (float i=-length+0.5;i<=length;i+=2.0) {
vec4 color = vec4(0.0);
for (float i=-ceillen+0.5;i<=ceillen;i+=2.0) {
float y = sin_angle * i;
float x = cos_angle * i;
gl_FragColor += texture2D(image, vec2(gl_FragCoord.x+x, gl_FragCoord.y+y)/resolution)*(divider);
color += texture2D(image, vec2(gl_FragCoord.x+x, gl_FragCoord.y+y)/resolution)*(divider);
}
gl_FragColor = color;
} else {
gl_FragColor = texture2D(image, gl_FragCoord.xy/resolution);
}
+4 -2
View File
@@ -24,7 +24,7 @@ void main(void) {
if (radius == 0.0 || sigma == 0.0 || (!horiz_blur && !vert_blur)) {
gl_FragColor = texture2D(image, gl_FragCoord.xy/resolution);
} else {
float rad = floor(radius);
float rad = ceil(radius);
float x_rad = horiz_blur ? rad : 0.5;
float y_rad = vert_blur ? rad : 0.5;
@@ -36,11 +36,13 @@ void main(void) {
}
}
vec4 color = vec4(0.0);
for (float x=-x_rad+0.5;x<=x_rad;x+=2.0) {
for (float y=-y_rad+0.5;y<=y_rad;y+=2.0) {
float weight = (gaussian2(x, y, sigma)/sum);
gl_FragColor += texture2D(image, (vec2(gl_FragCoord.x+x, gl_FragCoord.y+y))/resolution)*(weight);
color += texture2D(image, (vec2(gl_FragCoord.x+x, gl_FragCoord.y+y))/resolution)*(weight);
}
}
gl_FragColor = color;
}
}
+8 -8
View File
@@ -28,20 +28,20 @@ CornerPinEffect::CornerPinEffect(Clip *c, const EffectMeta *em) : Effect(c, em)
perspective->set_bool_value(true);
top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
top_left_gizmo->x_field = top_left_x;
top_left_gizmo->y_field = top_left_y;
top_left_gizmo->x_field1 = top_left_x;
top_left_gizmo->y_field1 = top_left_y;
top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
top_right_gizmo->x_field = top_right_x;
top_right_gizmo->y_field = top_right_y;
top_right_gizmo->x_field1 = top_right_x;
top_right_gizmo->y_field1 = top_right_y;
bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
bottom_left_gizmo->x_field = bottom_left_x;
bottom_left_gizmo->y_field = bottom_left_y;
bottom_left_gizmo->x_field1 = bottom_left_x;
bottom_left_gizmo->y_field1 = bottom_left_y;
bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
bottom_right_gizmo->x_field = bottom_right_x;
bottom_right_gizmo->y_field = bottom_right_y;
bottom_right_gizmo->x_field1 = bottom_right_x;
bottom_right_gizmo->y_field1 = bottom_right_y;
vertPath = "cornerpin.vert";
fragPath = "cornerpin.frag";
+38 -31
View File
@@ -12,7 +12,7 @@
#include "ui/collapsiblewidget.h"
#include "project/clip.h"
#include "project/sequence.h"
#include "io/media.h"
#include "project/footage.h"
#include "io/math.h"
#include "ui/labelslider.h"
#include "ui/comboboxex.h"
@@ -68,44 +68,51 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em)
// set up gizmos
top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
top_left_gizmo->set_cursor(Qt::SizeFDiagCursor);
top_left_gizmo->x_field = scale_x;
top_left_gizmo->x_field1 = scale_x;
top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
top_center_gizmo->set_cursor(Qt::SizeVerCursor);
top_center_gizmo->y_field = scale_x;
top_center_gizmo->y_field1 = scale_x;
top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
top_right_gizmo->set_cursor(Qt::SizeBDiagCursor);
top_right_gizmo->x_field = scale_x;
top_right_gizmo->x_field1 = scale_x;
bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor);
bottom_left_gizmo->x_field = scale_x;
bottom_left_gizmo->x_field1 = scale_x;
bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
bottom_center_gizmo->set_cursor(Qt::SizeVerCursor);
bottom_center_gizmo->y_field = scale_x;
bottom_center_gizmo->y_field1 = scale_x;
bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor);
bottom_right_gizmo->x_field = scale_x;
bottom_right_gizmo->x_field1 = scale_x;
left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
left_center_gizmo->set_cursor(Qt::SizeHorCursor);
left_center_gizmo->x_field = scale_x;
left_center_gizmo->x_field1 = scale_x;
right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
right_center_gizmo->set_cursor(Qt::SizeHorCursor);
right_center_gizmo->x_field = scale_x;
right_center_gizmo->x_field1 = scale_x;
anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET);
anchor_gizmo->set_cursor(Qt::SizeAllCursor);
anchor_gizmo->x_field1 = anchor_x_box;
anchor_gizmo->y_field1 = anchor_y_box;
anchor_gizmo->x_field2 = position_x;
anchor_gizmo->y_field2 = position_y;
rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT);
rotate_gizmo->color = Qt::green;
rotate_gizmo->set_cursor(Qt::SizeAllCursor);
rotate_gizmo->x_field = rotation;
rotate_gizmo->x_field1 = rotation;
rect_gizmo = add_gizmo(GIZMO_TYPE_POLY);
rect_gizmo->x_field = position_x;
rect_gizmo->y_field = position_y;
rect_gizmo->x_field1 = position_x;
rect_gizmo->y_field1 = position_y;
connect(uniform_scale_field, SIGNAL(toggled(bool)), this, SLOT(toggle_uniform_scale(bool)));
@@ -137,31 +144,31 @@ void TransformEffect::refresh() {
double x_percent_multipler = 200.0 / parent_clip->sequence->width;
double y_percent_multipler = 200.0 / parent_clip->sequence->height;
top_left_gizmo->x_field_multi = -x_percent_multipler;
top_left_gizmo->y_field_multi = -y_percent_multipler;
top_center_gizmo->y_field_multi = -y_percent_multipler;
top_right_gizmo->x_field_multi = x_percent_multipler;
top_right_gizmo->y_field_multi = -y_percent_multipler;
bottom_left_gizmo->x_field_multi = -x_percent_multipler;
bottom_left_gizmo->y_field_multi = y_percent_multipler;
bottom_center_gizmo->y_field_multi = y_percent_multipler;
bottom_right_gizmo->x_field_multi = x_percent_multipler;
bottom_right_gizmo->y_field_multi = y_percent_multipler;
left_center_gizmo->x_field_multi = -x_percent_multipler;
right_center_gizmo->x_field_multi = x_percent_multipler;
rotate_gizmo->x_field_multi = x_percent_multipler;
top_left_gizmo->x_field_multi1 = -x_percent_multipler;
top_left_gizmo->y_field_multi1 = -y_percent_multipler;
top_center_gizmo->y_field_multi1 = -y_percent_multipler;
top_right_gizmo->x_field_multi1 = x_percent_multipler;
top_right_gizmo->y_field_multi1 = -y_percent_multipler;
bottom_left_gizmo->x_field_multi1 = -x_percent_multipler;
bottom_left_gizmo->y_field_multi1 = y_percent_multipler;
bottom_center_gizmo->y_field_multi1 = y_percent_multipler;
bottom_right_gizmo->x_field_multi1 = x_percent_multipler;
bottom_right_gizmo->y_field_multi1 = y_percent_multipler;
left_center_gizmo->x_field_multi1 = -x_percent_multipler;
right_center_gizmo->x_field_multi1 = x_percent_multipler;
rotate_gizmo->x_field_multi1 = x_percent_multipler;
}
}
void TransformEffect::toggle_uniform_scale(bool enabled) {
scale_y->set_enabled(!enabled);
top_center_gizmo->y_field = enabled ? scale_x : scale_y;
bottom_center_gizmo->y_field = enabled ? scale_x : scale_y;
top_left_gizmo->y_field = enabled ? NULL : scale_y;
top_right_gizmo->y_field = enabled ? NULL : scale_y;
bottom_left_gizmo->y_field = enabled ? NULL : scale_y;
bottom_right_gizmo->y_field = enabled ? NULL : scale_y;
top_center_gizmo->y_field1 = enabled ? scale_x : scale_y;
bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y;
top_left_gizmo->y_field1 = enabled ? NULL : scale_y;
top_right_gizmo->y_field1 = enabled ? NULL : scale_y;
bottom_left_gizmo->y_field1 = enabled ? NULL : scale_y;
bottom_right_gizmo->y_field1 = enabled ? NULL : scale_y;
}
void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, int data) {
+1
View File
@@ -33,6 +33,7 @@ private:
EffectGizmo* bottom_right_gizmo;
EffectGizmo* left_center_gizmo;
EffectGizmo* right_center_gizmo;
EffectGizmo* anchor_gizmo;
EffectGizmo* rotate_gizmo;
EffectGizmo* rect_gizmo;
+5 -1
View File
@@ -22,11 +22,15 @@ void main(void) {
float limit = ceil(radius * multiplier);
float divider = 1.0 / limit;
vec4 color = vec4(0.0);
for (float i=-limit+0.5;i<=limit;i+=2.0) {
float y = sin_angle * i;
float x = cos_angle * i;
gl_FragColor += texture2D(image, vec2(gl_FragCoord.x+x, gl_FragCoord.y+y)/resolution)*(divider);
color += texture2D(image, vec2(gl_FragCoord.x+x, gl_FragCoord.y+y)/resolution)*(divider);
}
gl_FragColor = color;
} else {
gl_FragColor = texture2D(image, gl_FragCoord.xy/resolution);
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+1
View File
@@ -48,5 +48,6 @@
<file>record-disabled.png</file>
<file>transition-tool.png</file>
<file>transition-tool-disabled.png</file>
<file>error.png</file>
</qresource>
</RCC>
+19
View File
@@ -0,0 +1,19 @@
#include "avtogl.h"
extern "C" {
#include <libavutil/avutil.h>
}
enum QOpenGLTexture::PixelFormat get_gl_pix_fmt_from_av(int format) {
switch (format) {
case AV_PIX_FMT_RGB24: return QOpenGLTexture::RGB;
}
return QOpenGLTexture::RGBA;
}
enum QOpenGLTexture::TextureFormat get_gl_tex_fmt_from_av(int format) {
switch (format) {
case AV_PIX_FMT_RGB24: return QOpenGLTexture::RGB8_UNorm;
}
return QOpenGLTexture::RGBA8_UNorm;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef AVTOGL_H
#define AVTOGL_H
#include <QOpenGLTexture>
enum QOpenGLTexture::PixelFormat get_gl_pix_fmt_from_av(int format);
enum QOpenGLTexture::TextureFormat get_gl_tex_fmt_from_av(int format);
#endif // AVTOGL_H
+1
View File
@@ -1,6 +1,7 @@
#include "clipboard.h"
#include "project/clip.h"
#include "project/effect.h"
int clipboard_type = CLIPBOARD_TYPE_CLIP;
QVector<void*> clipboard;
+16 -1
View File
@@ -29,7 +29,10 @@ Config::Config()
enable_seek_to_import(false),
enable_audio_scrubbing(true),
drop_on_media_to_replace(true),
autoscroll(AUTOSCROLL_PAGE_SCROLL)
autoscroll(AUTOSCROLL_PAGE_SCROLL),
audio_rate(48000),
fast_seeking(false),
hover_focus(false)
{}
void Config::load(QString path) {
@@ -103,6 +106,15 @@ void Config::load(QString path) {
} else if (stream.name() == "Autoscroll") {
stream.readNext();
autoscroll = stream.text().toInt();
} else if (stream.name() == "AudioRate") {
stream.readNext();
audio_rate = stream.text().toInt();
} else if (stream.name() == "FastSeeking") {
stream.readNext();
fast_seeking = (stream.text() == "1");
} else if (stream.name() == "HoverFocus") {
stream.readNext();
hover_focus = (stream.text() == "1");
}
}
}
@@ -148,6 +160,9 @@ void Config::save(QString path) {
stream.writeTextElement("AudioScrubbing", QString::number(enable_audio_scrubbing));
stream.writeTextElement("DropFileOnMediaToReplace", QString::number(drop_on_media_to_replace));
stream.writeTextElement("Autoscroll", QString::number(autoscroll));
stream.writeTextElement("AudioRate", QString::number(audio_rate));
stream.writeTextElement("FastSeeking", QString::number(fast_seeking));
stream.writeTextElement("HoverFocus", QString::number(hover_focus));
stream.writeEndElement(); // configuration
stream.writeEndDocument(); // doc
+3
View File
@@ -41,6 +41,9 @@ struct Config {
bool enable_audio_scrubbing;
bool drop_on_media_to_replace;
int autoscroll;
int audio_rate;
bool fast_seeking;
bool hover_focus;
void load(QString path);
void save(QString path);
+24 -10
View File
@@ -122,13 +122,14 @@ bool ExportThread::setupVideo() {
if (vcodec_ctx->codec_id == AV_CODEC_ID_H264) {
/*char buffer[50];
itoa(vcodec_ctx, buffer, 10);*/
itoa(vcodec_ctx, buffer, 10);*/
// av_opt_set(vcodec_ctx->priv_data, "preset", "slow", AV_OPT_SEARCH_CHILDREN);
//av_opt_set(vcodec_ctx->priv_data, "preset", "fast", AV_OPT_SEARCH_CHILDREN);
av_opt_set(vcodec_ctx->priv_data, "x264opts", "opencl", AV_OPT_SEARCH_CHILDREN);
switch (video_compression_type) {
case COMPRESSION_TYPE_CFR:
av_opt_set(vcodec_ctx->priv_data, "crf", QString::number(static_cast<int>(video_bitrate)).toLatin1(), AV_OPT_SEARCH_CHILDREN);
av_opt_set(vcodec_ctx->priv_data, "crf", QString::number(static_cast<int>(video_bitrate)).toUtf8(), AV_OPT_SEARCH_CHILDREN);
break;
}
}
@@ -316,7 +317,7 @@ void ExportThread::run() {
}
// copy filename
QByteArray ba = filename.toLatin1();
QByteArray ba = filename.toUtf8();
c_filename = new char[ba.size()+1];
strcpy(c_filename, ba.data());
@@ -344,8 +345,12 @@ void ExportThread::run() {
panel_sequence_viewer->viewer_widget->default_fbo = &fbo;
long file_audio_samples = 0;
qint64 start_time, frame_time, avg_time, eta, total_time = 0;
long remaining_frames, frame_count = 1;
while (sequence->playhead < end_frame && continueEncode) {
start_time = QDateTime::currentMSecsSinceEpoch();
panel_sequence_viewer->viewer_widget->paintGL();
double timecode_secs = (double) (sequence->playhead-start_frame) / sequence->frame_rate;
@@ -357,10 +362,8 @@ void ExportThread::run() {
sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize);
sws_frame->pts = round(timecode_secs/av_q2d(video_stream->time_base));
// send to encoder
// dout << "starting encode of video frame" << sequence->playhead;
if (!encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream)) continueEncode = false;
// dout << "completed encode of video frame" << sequence->playhead;
// send to encoder
if (!encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream)) continueEncode = false;
}
if (audio_enabled) {
// do we need to encode more audio samples?
@@ -389,8 +392,19 @@ void ExportThread::run() {
file_audio_samples += swr_frame->nb_samples;
}
}
emit progress_changed(((double) (sequence->playhead-start_frame) / (double) (end_frame-start_frame)) * 100);
// encoding stats
frame_time = (QDateTime::currentMSecsSinceEpoch()-start_time);
total_time += frame_time;
remaining_frames = (end_frame-sequence->playhead);
avg_time = (total_time/frame_count);
eta = (remaining_frames*avg_time);
// dout << "[INFO] Encoded frame" << sequence->playhead << "- took" << frame_time << "ms (avg:" << avg_time << "ms, remaining:" << remaining_frames << ", ETA:" << eta << ")";
emit progress_changed(qRound(((double) (sequence->playhead-start_frame) / (double) (end_frame-start_frame)) * 100), eta);
sequence->playhead++;
frame_count++;
}
panel_sequence_viewer->viewer_widget->default_fbo = NULL;
@@ -425,7 +439,7 @@ void ExportThread::run() {
continueEncode = false;
}
emit progress_changed(100);
emit progress_changed(100, 0);
}
avio_closep(&fmt_ctx->pb);
+1 -1
View File
@@ -44,7 +44,7 @@ public:
bool continueEncode;
signals:
void progress_changed(int value);
void progress_changed(int value, qint64 remaining_ms);
private:
bool encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream);
bool setupVideo();
+734
View File
@@ -0,0 +1,734 @@
#include "loadthread.h"
#include "mainwindow.h"
#include "panels/panels.h"
#include "panels/project.h"
#include "project/footage.h"
#include "io/config.h"
#include "project/clip.h"
#include "project/sequence.h"
#include "project/transition.h"
#include "project/effect.h"
#include "playback/playback.h"
#include "io/previewgenerator.h"
#include "dialogs/loaddialog.h"
#include "project/media.h"
#include "debug.h"
#include <QFile>
#include <QMessageBox>
#include <QTreeWidgetItem>
struct TransitionData {
int id;
QString name;
long length;
Clip* otc;
Clip* ctc;
};
LoadThread::LoadThread(LoadDialog* l, bool a) : ld(l), autorecovery(a), cancelled(false) {
connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
connect(this, SIGNAL(success()), this, SLOT(success_func()));
connect(this, SIGNAL(error()), this, SLOT(error_func()));
connect(this, SIGNAL(start_create_dual_transition(const TransitionData*,Clip*,Clip*,const EffectMeta*)), this, SLOT(create_dual_transition(const TransitionData*,Clip*,Clip*,const EffectMeta*)));
connect(this, SIGNAL(start_create_effect_ui(QXmlStreamReader*, Clip*, int, const EffectMeta*, long, bool)), this, SLOT(create_effect_ui(QXmlStreamReader*, Clip*, int, const EffectMeta*, long, bool)));
}
const EffectMeta* get_meta_from_name(const QString& name, int type) {
for (int j=0;j<effects.size();j++) {
if (effects.at(j).name == name) {
return &effects.at(j);
}
}
return NULL;
}
void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) {
int effect_id = -1;
QString effect_name;
bool effect_enabled = true;
long effect_length = -1;
for (int j=0;j<stream.attributes().size();j++) {
const QXmlStreamAttribute& attr = stream.attributes().at(j);
if (attr.name() == "id") {
effect_id = attr.value().toInt();
} else if (attr.name() == "enabled") {
effect_enabled = (attr.value() == "1");
} else if (attr.name() == "name") {
effect_name = attr.value().toString();
} else if (attr.name() == "length") {
effect_length = attr.value().toLong();
}
}
// backwards compatibility with 180820
if (stream.name() == "effect" && effect_id != -1) {
switch (effect_id) {
case 0: effect_name = (c->track < 0) ? "Transform" : "Volume"; break;
case 1: effect_name = (c->track < 0) ? "Shake" : "Pan"; break;
case 2: effect_name = (c->track < 0) ? "Text" : "Noise"; break;
case 3: effect_name = (c->track < 0) ? "Solid" : "Tone"; break;
case 4: effect_name = "Invert"; break;
case 5: effect_name = "Chroma Key"; break;
case 6: effect_name = "Gaussian Blur"; break;
case 7: effect_name = "Crop"; break;
case 8: effect_name = "Flip"; break;
case 9: effect_name = "Box Blur"; break;
case 10: effect_name = "Wave"; break;
case 11: effect_name = "Temperature"; break;
}
}
// wait for effects to be loaded
effects_loaded.lock();
const EffectMeta* meta = NULL;
// find effect with this name
if (!effect_name.isEmpty()) {
meta = get_meta_from_name(effect_name, (c->track < 0) ? EFFECT_TYPE_VIDEO : EFFECT_TYPE_AUDIO);
}
effects_loaded.unlock();
if (meta == NULL) {
dout << "[WARNING] An effect used by this project is missing. It was not loaded.";
} else {
QString tag = stream.name().toString();
int type;
if (tag == "opening") {
type = TA_OPENING_TRANSITION;
} else if (tag == "closing") {
type = TA_CLOSING_TRANSITION;
} else {
type = TA_NO_TRANSITION;
}
emit start_create_effect_ui(&stream, c, type, meta, effect_length, effect_enabled);
waitCond.wait(&mutex);
}
}
void LoadThread::read_next(QXmlStreamReader &stream) {
stream.readNext();
update_current_element_count(stream);
}
void LoadThread::read_next_start_element(QXmlStreamReader &stream) {
stream.readNextStartElement();
update_current_element_count(stream);
}
void LoadThread::update_current_element_count(QXmlStreamReader &stream) {
if (is_element(stream)) {
current_element_count++;
report_progress((current_element_count * 100) / total_element_count);
}
}
bool LoadThread::is_element(QXmlStreamReader &stream) {
return stream.isStartElement()
&& (stream.name() == "folder"
|| stream.name() == "footage"
|| stream.name() == "sequence"
|| stream.name() == "clip"
|| stream.name() == "effect");
}
bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
f.seek(0);
stream.setDevice(stream.device());
QString root_search;
QString child_search;
switch (type) {
case LOAD_TYPE_VERSION:
root_search = "version";
break;
case LOAD_TYPE_URL:
root_search = "url";
break;
case MEDIA_TYPE_FOLDER:
root_search = "folders";
child_search = "folder";
break;
case MEDIA_TYPE_FOOTAGE:
root_search = "media";
child_search = "footage";
break;
case MEDIA_TYPE_SEQUENCE:
root_search = "sequences";
child_search = "sequence";
break;
}
show_err = true;
while (!stream.atEnd() && !cancelled) {
read_next_start_element(stream);
if (stream.name() == root_search) {
if (type == LOAD_TYPE_VERSION) {
int proj_version = stream.readElementText().toInt();
if (proj_version < MIN_SAVE_VERSION && proj_version > SAVE_VERSION) {
if (QMessageBox::warning(mainWindow, "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) {
show_err = false;
return false;
}
}
} else if (type == LOAD_TYPE_URL) {
internal_proj_url = stream.readElementText();
internal_proj_dir = QFileInfo(internal_proj_url).absoluteDir();
} else {
while (!cancelled && !(stream.name() == root_search && stream.isEndElement())) {
read_next(stream);
if (stream.name() == child_search && stream.isStartElement()) {
switch (type) {
case MEDIA_TYPE_FOLDER:
{
Media* folder = panel_project->new_folder(0);
folder->temp_id2 = 0;
for (int j=0;j<stream.attributes().size();j++) {
const QXmlStreamAttribute& attr = stream.attributes().at(j);
if (attr.name() == "id") {
folder->temp_id = attr.value().toInt();
} else if (attr.name() == "name") {
folder->set_name(attr.value().toString());
} else if (attr.name() == "parent") {
folder->temp_id2 = attr.value().toInt();
}
}
loaded_folders.append(folder);
}
break;
case MEDIA_TYPE_FOOTAGE:
{
int folder = 0;
Media* item = new Media(0);
Footage* m = new Footage();
m->using_inout = false;
for (int j=0;j<stream.attributes().size();j++) {
const QXmlStreamAttribute& attr = stream.attributes().at(j);
if (attr.name() == "id") {
m->save_id = attr.value().toInt();
} else if (attr.name() == "folder") {
folder = attr.value().toInt();
} else if (attr.name() == "name") {
m->name = attr.value().toString();
} else if (attr.name() == "url") {
m->url = attr.value().toString();
if (!QFileInfo::exists(m->url)) { // if path is not absolute
QString proj_dir_test = proj_dir.absoluteFilePath(m->url);
QString internal_proj_dir_test = internal_proj_dir.absoluteFilePath(m->url);
if (QFileInfo::exists(proj_dir_test)) { // if path is relative to the project's current dir
m->url = proj_dir_test;
dout << "[INFO] Matched" << attr.value().toString() << "relative to project's current directory";
} else if (QFileInfo::exists(internal_proj_dir_test)) { // if path is relative to the last directory the project was saved in
m->url = internal_proj_dir_test;
dout << "[INFO] Matched" << attr.value().toString() << "relative to project's internal directory";
} else if (m->url.contains('%')) {
// hack for image sequences (qt won't be able to find the URL with %, but ffmpeg may)
m->url = proj_dir_test;
dout << "[INFO] Guess image sequence" << attr.value().toString() << "path to project's current directory";
} else {
dout << "[INFO] Failed to match" << attr.value().toString() << "to file";
}
} else {
dout << "[INFO] Matched" << attr.value().toString() << "with absolute path";
}
} else if (attr.name() == "duration") {
m->length = attr.value().toLongLong();
} else if (attr.name() == "using_inout") {
m->using_inout = (attr.value() == "1");
} else if (attr.name() == "in") {
m->in = attr.value().toLong();
} else if (attr.name() == "out") {
m->out = attr.value().toLong();
}
}
item->set_footage(m);
project_model.appendChild(find_loaded_folder_by_id(folder), item);
// analyze media to see if it's the same
loaded_media_items.append(item);
}
break;
case MEDIA_TYPE_SEQUENCE:
{
Media* parent = NULL;
Sequence* s = new Sequence();
// load attributes about sequence
for (int j=0;j<stream.attributes().size();j++) {
const QXmlStreamAttribute& attr = stream.attributes().at(j);
if (attr.name() == "name") {
s->name = attr.value().toString();
} else if (attr.name() == "folder") {
int folder = attr.value().toInt();
if (folder > 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") {
s->height = attr.value().toInt();
} else if (attr.name() == "framerate") {
s->frame_rate = attr.value().toDouble();
} else if (attr.name() == "afreq") {
s->audio_frequency = attr.value().toInt();
} else if (attr.name() == "alayout") {
s->audio_layout = attr.value().toInt();
} else if (attr.name() == "open") {
open_seq = s;
} else if (attr.name() == "workarea") {
s->using_workarea = (attr.value() == "1");
} else if (attr.name() == "workareaIn") {
s->workarea_in = attr.value().toLong();
} else if (attr.name() == "workareaOut") {
s->workarea_out = attr.value().toLong();
}
}
QVector<TransitionData> transition_data;
// load all clips and clip information
while (!cancelled && !(stream.name() == child_search && stream.isEndElement()) && !stream.atEnd()) {
read_next_start_element(stream);
if (stream.name() == "marker" && stream.isStartElement()) {
Marker m;
for (int j=0;j<stream.attributes().size();j++) {
const QXmlStreamAttribute& attr = stream.attributes().at(j);
if (attr.name() == "frame") {
m.frame = attr.value().toLong();
} else if (attr.name() == "name") {
m.name = attr.value().toString();
}
}
s->markers.append(m);
} else if (stream.name() == "transition" && stream.isStartElement()) {
TransitionData td;
td.otc = NULL;
td.ctc = NULL;
for (int j=0;j<stream.attributes().size();j++) {
const QXmlStreamAttribute& attr = stream.attributes().at(j);
if (attr.name() == "id") {
td.id = attr.value().toInt();
} else if (attr.name() == "name") {
td.name = attr.value().toString();
} else if (attr.name() == "length") {
td.length = attr.value().toLong();
}
}
transition_data.append(td);
} else if (stream.name() == "clip" && stream.isStartElement()) {
int media_type = -1;
int media_id, stream_id;
Clip* c = new Clip(s);
// backwards compatibility code
c->autoscale = false;
c->media = NULL;
for (int j=0;j<stream.attributes().size();j++) {
const QXmlStreamAttribute& attr = stream.attributes().at(j);
if (attr.name() == "name") {
c->name = attr.value().toString();
} else if (attr.name() == "enabled") {
c->enabled = (attr.value() == "1");
} else if (attr.name() == "id") {
c->load_id = attr.value().toInt();
} else if (attr.name() == "clipin") {
c->clip_in = attr.value().toLong();
} else if (attr.name() == "in") {
c->timeline_in = attr.value().toLong();
} else if (attr.name() == "out") {
c->timeline_out = attr.value().toLong();
} else if (attr.name() == "track") {
c->track = attr.value().toInt();
} else if (attr.name() == "r") {
c->color_r = attr.value().toInt();
} else if (attr.name() == "g") {
c->color_g = attr.value().toInt();
} else if (attr.name() == "b") {
c->color_b = attr.value().toInt();
} else if (attr.name() == "autoscale") {
c->autoscale = (attr.value() == "1");
} else if (attr.name() == "media") {
media_type = MEDIA_TYPE_FOOTAGE;
media_id = attr.value().toInt();
} else if (attr.name() == "stream") {
stream_id = attr.value().toInt();
} else if (attr.name() == "speed") {
c->speed = attr.value().toDouble();
} else if (attr.name() == "maintainpitch") {
c->maintain_audio_pitch = (attr.value() == "1");
} else if (attr.name() == "reverse") {
c->reverse = (attr.value() == "1");
} else if (attr.name() == "opening") {
c->opening_transition = attr.value().toInt();
} else if (attr.name() == "closing") {
c->closing_transition = attr.value().toInt();
} else if (attr.name() == "sequence") {
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
switch (media_type) {
case MEDIA_TYPE_FOOTAGE:
if (media_id >= 0) {
for (int j=0;j<loaded_media_items.size();j++) {
Footage* m = loaded_media_items.at(j)->to_footage();
if (m->save_id == media_id) {
c->media = loaded_media_items.at(j);
c->media_stream = stream_id;
break;
}
}
}
break;
}
// load links and effects
while (!cancelled && !(stream.name() == "clip" && stream.isEndElement()) && !stream.atEnd()) {
read_next(stream);
if (stream.isStartElement()) {
if (stream.name() == "linked") {
while (!cancelled && !(stream.name() == "linked" && stream.isEndElement()) && !stream.atEnd()) {
read_next(stream);
if (stream.name() == "link" && stream.isStartElement()) {
for (int k=0;k<stream.attributes().size();k++) {
const QXmlStreamAttribute& link_attr = stream.attributes().at(k);
if (link_attr.name() == "id") {
c->linked.append(link_attr.value().toInt());
break;
}
}
}
}
if (cancelled) return false;
} else if (stream.isStartElement() && (stream.name() == "effect" || stream.name() == "opening" || stream.name() == "closing")) {
// "opening" and "closing" are backwards compatibility code
load_effect(stream, c);
}
}
}
if (cancelled) return false;
s->clips.append(c);
}
}
if (cancelled) return false;
// correct links, clip IDs, transitions
for (int i=0;i<s->clips.size();i++) {
// correct links
Clip* correct_clip = s->clips.at(i);
for (int j=0;j<correct_clip->linked.size();j++) {
bool found = false;
for (int k=0;k<s->clips.size();k++) {
if (s->clips.at(k)->load_id == correct_clip->linked.at(j)) {
correct_clip->linked[j] = k;
found = true;
break;
}
}
if (!found) {
correct_clip->linked.removeAt(j);
j--;
if (QMessageBox::warning(mainWindow, "Invalid Clip Link", "This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
delete s;
return false;
}
}
}
// re-link clips to transitions
if (correct_clip->opening_transition > -1) {
for (int j=0;j<transition_data.size();j++) {
if (transition_data.at(j).id == correct_clip->opening_transition) {
transition_data[j].otc = correct_clip;
}
}
}
if (correct_clip->closing_transition > -1) {
for (int j=0;j<transition_data.size();j++) {
if (transition_data.at(j).id == correct_clip->closing_transition) {
transition_data[j].ctc = correct_clip;
}
}
}
}
// create transitions
for (int i=0;i<transition_data.size();i++) {
const TransitionData& td = transition_data.at(i);
Clip* primary = td.otc;
Clip* secondary = td.ctc;
if (primary != NULL || secondary != NULL) {
if (primary == NULL) {
primary = secondary;
secondary = NULL;
}
const EffectMeta* meta = get_meta_from_name(td.name, (primary->track < 0) ? EFFECT_TYPE_VIDEO : EFFECT_TYPE_AUDIO);
if (meta == NULL) {
dout << "[WARNING] Failed to link transition with name:" << td.name;
if (td.otc != NULL) td.otc->opening_transition = -1;
if (td.ctc != NULL) td.ctc->closing_transition = -1;
} else {
emit start_create_dual_transition(&td, primary, secondary, meta);
waitCond.wait(&mutex);
}
}
}
Media* m = panel_project->new_sequence(NULL, s, false, parent);
loaded_sequences.append(m);
}
break;
}
}
}
if (cancelled) return false;
}
break;
}
}
return !cancelled;
}
Media* LoadThread::find_loaded_folder_by_id(int id) {
if (id == 0) return NULL;
for (int j=0;j<loaded_folders.size();j++) {
Media* parent_item = loaded_folders.at(j);
if (parent_item->temp_id == id) {
return parent_item;
}
}
return NULL;
}
void LoadThread::run() {
mutex.lock();
QFile file(project_url);
if (!file.open(QIODevice::ReadOnly)) {
dout << "[ERROR] Could not open file";
return;
}
/* set up directories to search for media
* most of the time, these will be the same but in
* case the project file has moved without the footage,
* we check both
*/
proj_dir = QFileInfo(project_url).absoluteDir();
internal_proj_dir = QFileInfo(project_url).absoluteDir();
internal_proj_url = project_url;
QXmlStreamReader stream(&file);
bool cont = false;
error_str.clear();
show_err = true;
// temp variables for loading (unnecessary?)
open_seq = NULL;
loaded_folders.clear();
loaded_media_items.clear();
loaded_clips.clear();
loaded_sequences.clear();
// get "element" count
current_element_count = 0;
total_element_count = 0;
while (!cancelled && !stream.atEnd()) {
stream.readNextStartElement();
if (is_element(stream)) {
total_element_count++;
}
}
cont = !cancelled;
// find project file version
cont = load_worker(file, stream, LOAD_TYPE_VERSION);
// find project's internal URL
cont = load_worker(file, stream, LOAD_TYPE_URL);
// load folders first
if (cont) {
cont = load_worker(file, stream, MEDIA_TYPE_FOLDER);
}
// load media
if (cont) {
// since folders loaded correctly, organize them appropriately
for (int i=0;i<loaded_folders.size();i++) {
Media* folder = loaded_folders.at(i);
int parent = folder->temp_id2;
project_model.appendChild(find_loaded_folder_by_id(parent), folder);
}
cont = load_worker(file, stream, MEDIA_TYPE_FOOTAGE);
}
// load sequences
if (cont) {
cont = load_worker(file, stream, MEDIA_TYPE_SEQUENCE);
}
if (!cancelled) {
if (!cont) {
xml_error = false;
if (show_err) emit error();
} else if (stream.hasError()) {
error_str = stream.errorString();
xml_error = true;
emit error();
cont = false;
} else {
// attach nested sequence clips to their sequences
for (int i=0;i<loaded_clips.size();i++) {
for (int j=0;j<loaded_sequences.size();j++) {
if (loaded_clips.at(i)->media == NULL && loaded_clips.at(i)->media_stream == loaded_sequences.at(j)->to_sequence()->save_id) {
loaded_clips.at(i)->media = loaded_sequences.at(j);
loaded_clips.at(i)->refresh();
break;
}
}
}
}
}
if (cont) {
emit success(); // run in main thread
for (int i=0;i<loaded_media_items.size();i++) {
panel_project->start_preview_generator(loaded_media_items.at(i), true);
}
}
file.close();
mutex.unlock();
}
void LoadThread::cancel() {
waitCond.wakeAll();
cancelled = true;
}
void LoadThread::error_func() {
if (xml_error) {
dout << "[ERROR] Error parsing XML." << error_str;
QMessageBox::critical(mainWindow, "XML Parsing Error", "Couldn't load '" + project_url + "'. " + error_str, QMessageBox::Ok);
} else {
QMessageBox::critical(mainWindow, "Project Load Error", "Error loading project: " + error_str, QMessageBox::Ok);
}
}
void LoadThread::success_func() {
if (autorecovery) {
QString orig_filename = internal_proj_url;
int insert_index = internal_proj_url.lastIndexOf(".ove", -1, Qt::CaseInsensitive);
if (insert_index == -1) insert_index = internal_proj_url.length();
int counter = 1;
while (QFileInfo::exists(orig_filename)) {
orig_filename = internal_proj_url;
QString recover_text = "recovered";
if (counter > 1) {
recover_text += " " + QString::number(counter);
}
orig_filename.insert(insert_index, " (" + recover_text + ")");
counter++;
}
mainWindow->updateTitle(orig_filename);
} else {
panel_project->add_recent_project(project_url);
}
mainWindow->setWindowModified(autorecovery);
if (open_seq != NULL) set_sequence(open_seq);
update_ui(false);
}
void LoadThread::create_effect_ui(
QXmlStreamReader* stream,
Clip* c,
int type,
const EffectMeta* meta,
long effect_length,
bool effect_enabled)
{
/* This is extremely hacky - prepare yourself.
*
* When moving project loading to a separate thread, it was soon discovered
* that effects wouldn't load correctly anymore. They were actually still
* "functional", but there were no controls appearing in EffectControls.
*
* Turns out since Effect creates its UI in its constructor, the UI was
* created in this thread rather than the main GUI thread, which is a big
* no-no. Unfortunately the design of Effect does not separate UI and data,
* so having the UI set up was integral to creating annd loading the effect.
*
* Therefore, rather than rewrite the class (I just rewrote QTreeWidget to
* QTreeView with a custom model/item so I'm exhausted), for
* quick-n-dirty-ness, I made LoadThread offload the effect creation to the
* main thread (and since the effect loads data from the same XML stream,
* the LoadThread has to wait for the effect to finish before it can
* continue.
*
* Sorry. I'll fix it one day.
*/
if (cancelled) return;
if (type == TA_NO_TRANSITION) {
Effect* e = create_effect(c, meta);
e->set_enabled(effect_enabled);
e->load(*stream);
c->effects.append(e);
} else {
int transition_index = create_transition(c, NULL, meta);
Transition* t = c->sequence->transitions.at(transition_index);
if (effect_length > -1) t->set_length(effect_length);
t->set_enabled(effect_enabled);
t->load(*stream);
if (type == TA_OPENING_TRANSITION) {
c->opening_transition = transition_index;
} else {
c->closing_transition = transition_index;
}
}
waitCond.wakeAll();
}
void LoadThread::create_dual_transition(const TransitionData* td, Clip* primary, Clip* secondary, const EffectMeta* meta) {
int transition_index = create_transition(primary, secondary, meta);
primary->sequence->transitions.at(transition_index)->set_length(td->length);
if (td->otc != NULL) td->otc->opening_transition = transition_index;
if (td->ctc != NULL) td->ctc->closing_transition = transition_index;
waitCond.wakeAll();
}
+72
View File
@@ -0,0 +1,72 @@
#ifndef LOADTHREAD_H
#define LOADTHREAD_H
#include <QThread>
#include <QDir>
#include <QXmlStreamReader>
#include <QMutex>
#include <QWaitCondition>
class Media;
struct Footage;
struct Clip;
struct Sequence;
class LoadDialog;
class TransitionData;
struct EffectMeta;
class LoadThread : public QThread
{
Q_OBJECT
public:
LoadThread(LoadDialog* l, bool a);
void run();
void cancel();
signals:
void success();
void error();
void start_create_effect_ui(QXmlStreamReader* stream, Clip* c, int type, const EffectMeta* meta, long effect_length, bool effect_enabled);
void start_create_dual_transition(const TransitionData* td, Clip* primary, Clip* secondary, const EffectMeta* meta);
void report_progress(int p);
private slots:
void error_func();
void success_func();
void create_effect_ui(QXmlStreamReader* stream, Clip* c, int type, const EffectMeta* meta, long effect_length, bool effect_enabled);
void create_dual_transition(const TransitionData* td, Clip* primary, Clip* secondary, const EffectMeta* meta);
private:
LoadDialog* ld;
bool autorecovery;
bool load_worker(QFile& f, QXmlStreamReader& stream, int type);
void load_effect(QXmlStreamReader& stream, Clip* c);
void read_next(QXmlStreamReader& stream);
void read_next_start_element(QXmlStreamReader& stream);
void update_current_element_count(QXmlStreamReader& stream);
Sequence* open_seq;
QVector<Media*> loaded_media_items;
QDir proj_dir;
QDir internal_proj_dir;
QString internal_proj_url;
bool show_err;
QString error_str;
bool is_element(QXmlStreamReader& stream);
QVector<Media*> loaded_folders;
QVector<Clip*> loaded_clips;
QVector<Media*> loaded_sequences;
Media* find_loaded_folder_by_id(int id);
int current_element_count;
int total_element_count;
QMutex mutex;
QWaitCondition waitCond;
bool cancelled;
bool xml_error;
};
#endif // LOADTHREAD_H
+2
View File
@@ -8,6 +8,8 @@
QString real_app_dir;
QString get_effects_dir() {
QString env_path(qgetenv("OLIVE_EFFECTS_PATH"));
if (!env_path.isEmpty()) return env_path;
return get_app_dir() + "/effects";
}
+63 -55
View File
@@ -1,6 +1,7 @@
#include "previewgenerator.h"
#include "media.h"
#include "project/media.h"
#include "project/footage.h"
#include "panels/viewer.h"
#include "panels/project.h"
#include "io/config.h"
@@ -28,11 +29,11 @@ extern "C" {
QSemaphore sem(5); // only 5 preview generators can run at one time
PreviewGenerator::PreviewGenerator(QTreeWidgetItem* i, Media* m, bool r) :
PreviewGenerator::PreviewGenerator(Media* i, Footage* m, bool r) :
QThread(0),
fmt_ctx(NULL),
item(i),
media(m),
media(i),
footage(m),
retrieve_duration(false),
contains_still_image(false),
replace(r),
@@ -52,12 +53,12 @@ void PreviewGenerator::parse_media() {
for (int i=0;i<(int)fmt_ctx->nb_streams;i++) {
// Find the decoder for the video stream
if (avcodec_find_decoder(fmt_ctx->streams[i]->codecpar->codec_id) == NULL) {
dout << "[ERROR] Unsupported codec in stream" << i << "of file" << media->name;
dout << "[ERROR] Unsupported codec in stream" << i << "of file" << footage->name;
} else {
MediaStream* ms = media->get_stream_from_file_index(fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, i);
FootageStream* ms = footage->get_stream_from_file_index(fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, i);
bool append = false;
if (ms == NULL) {
ms = new MediaStream();
ms = new FootageStream();
ms->preview_done = false;
ms->file_index = i;
append = true;
@@ -93,18 +94,18 @@ void PreviewGenerator::parse_media() {
ms->video_auto_interlacing = VIDEO_PROGRESSIVE;
ms->video_interlacing = VIDEO_PROGRESSIVE;
if (append) media->video_tracks.append(ms);
if (append) footage->video_tracks.append(ms);
} else if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
ms->audio_channels = fmt_ctx->streams[i]->codecpar->channels;
ms->audio_layout = fmt_ctx->streams[i]->codecpar->channel_layout;
ms->audio_frequency = fmt_ctx->streams[i]->codecpar->sample_rate;
if (append) media->audio_tracks.append(ms);
if (append) footage->audio_tracks.append(ms);
} else if (append) {
delete ms;
}
}
}
media->length = fmt_ctx->duration;
footage->length = fmt_ctx->duration;
if (fmt_ctx->duration == INT64_MIN) {
retrieve_duration = true;
@@ -116,28 +117,29 @@ void PreviewGenerator::parse_media() {
bool PreviewGenerator::retrieve_preview(const QString& hash) {
// returns true if generate_waveform must be run, false if we got all previews from cached files
if (retrieve_duration) {
//dout << "[NOTE] " << media->name << "needs to retrieve duration";
return true;
}
bool found = true;
for (int i=0;i<media->video_tracks.size();i++) {
MediaStream* ms = media->video_tracks.at(i);
for (int i=0;i<footage->video_tracks.size();i++) {
FootageStream* ms = footage->video_tracks.at(i);
QString thumb_path = get_thumbnail_path(hash, ms);
QFile f(thumb_path);
if (f.exists() && ms->video_preview.load(thumb_path)) {
//dout << "loaded thumb" << ms->file_index << "from" << thumb_path;
//dout << "loaded thumb" << ms->file_index << "from" << thumb_path;
ms->preview_done = true;
} else {
found = false;
break;
}
}
for (int i=0;i<media->audio_tracks.size();i++) {
MediaStream* ms = media->audio_tracks.at(i);
for (int i=0;i<footage->audio_tracks.size();i++) {
FootageStream* ms = footage->audio_tracks.at(i);
QString waveform_path = get_waveform_path(hash, ms);
QFile f(waveform_path);
if (f.exists()) {
//dout << "loaded wave" << ms->file_index << "from" << waveform_path;
//dout << "loaded wave" << ms->file_index << "from" << waveform_path;
f.open(QFile::ReadOnly);
QByteArray data = f.readAll();
ms->audio_preview.resize(data.size());
@@ -153,12 +155,12 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) {
}
}
if (!found) {
for (int i=0;i<media->video_tracks.size();i++) {
MediaStream* ms = media->video_tracks.at(i);
for (int i=0;i<footage->video_tracks.size();i++) {
FootageStream* ms = footage->video_tracks.at(i);
ms->preview_done = false;
}
for (int i=0;i<media->audio_tracks.size();i++) {
MediaStream* ms = media->audio_tracks.at(i);
for (int i=0;i<footage->audio_tracks.size();i++) {
FootageStream* ms = footage->audio_tracks.at(i);
ms->audio_preview.clear();
ms->preview_done = false;
}
@@ -167,11 +169,11 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) {
}
void PreviewGenerator::finalize_media() {
media->ready_lock.unlock();
media->ready = true;
footage->ready_lock.unlock();
footage->ready = true;
if (!cancelled) {
if (media->video_tracks.size() == 0) {
if (footage->video_tracks.size() == 0) {
emit set_icon(ICON_TYPE_AUDIO, replace);
} else if (contains_still_image) {
emit set_icon(ICON_TYPE_IMAGE, replace);
@@ -179,7 +181,7 @@ void PreviewGenerator::finalize_media() {
emit set_icon(ICON_TYPE_VIDEO, replace);
}
if (!contains_still_image || media->audio_tracks.size() > 0) {
/*if (!contains_still_image || media->audio_tracks.size() > 0) {
double frame_rate = 30;
if (!contains_still_image && media->video_tracks.size() > 0) frame_rate = media->video_tracks.at(0)->video_frame_rate;
item->setText(1, frame_to_timecode(media->get_length_in_frames(frame_rate), config.timecode_view, frame_rate));
@@ -189,7 +191,7 @@ void PreviewGenerator::finalize_media() {
} else {
item->setText(2, QString::number(media->audio_tracks.at(0)->audio_frequency) + " Hz");
}
}
}*/
}
}
@@ -229,6 +231,9 @@ void PreviewGenerator::generate_waveform() {
while (codec_ctx[packet->stream_index] == NULL || avcodec_receive_frame(codec_ctx[packet->stream_index], temp_frame) == AVERROR(EAGAIN)) {
av_packet_unref(packet);
int read_ret = av_read_frame(fmt_ctx, packet);
//dout << "read frame for" << footage->name << footage->url << read_ret << "retrieve_duration:" << retrieve_duration << "eof:" << end_of_file << "packet pts:" << packet->pts;
if (read_ret < 0) {
end_of_file = true;
if (read_ret != AVERROR_EOF) dout << "[ERROR] Failed to read packet for preview generation" << read_ret;
@@ -244,7 +249,7 @@ void PreviewGenerator::generate_waveform() {
}
}
if (!end_of_file) {
MediaStream* s = media->get_stream_from_file_index(fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, packet->stream_index);
FootageStream* s = footage->get_stream_from_file_index(fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, packet->stream_index);
if (s != NULL) {
if (fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (!s->preview_done) {
@@ -349,10 +354,10 @@ void PreviewGenerator::generate_waveform() {
// check if we've got all our previews
if (retrieve_duration) {
done = false;
} else if (media->audio_tracks.size() == 0) {
} else if (footage->audio_tracks.size() == 0) {
done = true;
for (int i=0;i<media->video_tracks.size();i++) {
if (!media->video_tracks.at(i)->preview_done) {
for (int i=0;i<footage->video_tracks.size();i++) {
if (!footage->video_tracks.at(i)->preview_done) {
done = false;
break;
}
@@ -365,8 +370,8 @@ void PreviewGenerator::generate_waveform() {
av_packet_unref(packet);
}
}
for (int i=0;i<media->audio_tracks.size();i++) {
media->audio_tracks.at(i)->preview_done = true;
for (int i=0;i<footage->audio_tracks.size();i++) {
footage->audio_tracks.at(i)->preview_done = true;
}
av_frame_free(&temp_frame);
av_packet_free(&packet);
@@ -376,33 +381,33 @@ void PreviewGenerator::generate_waveform() {
}
}
if (retrieve_duration) {
media->length = 0;
footage->length = 0;
int maximum_stream = 0;
for (unsigned int i=0;i<fmt_ctx->nb_streams;i++) {
if (media_lengths[i] > media_lengths[maximum_stream]) {
maximum_stream = i;
}
}
media->length = (double) media_lengths[maximum_stream] / av_q2d(fmt_ctx->streams[maximum_stream]->avg_frame_rate) * AV_TIME_BASE; // TODO redo with PTS
footage->length = (double) media_lengths[maximum_stream] / av_q2d(fmt_ctx->streams[maximum_stream]->avg_frame_rate) * AV_TIME_BASE; // TODO redo with PTS
finalize_media();
}
delete [] media_lengths;
delete [] codec_ctx;
}
QString PreviewGenerator::get_thumbnail_path(const QString& hash, MediaStream* ms) {
QString PreviewGenerator::get_thumbnail_path(const QString& hash, FootageStream* ms) {
return data_path + "/" + hash + "t" + QString::number(ms->file_index);
}
QString PreviewGenerator::get_waveform_path(const QString& hash, MediaStream* ms) {
QString PreviewGenerator::get_waveform_path(const QString& hash, FootageStream* ms) {
return data_path + "/" + hash + "w" + QString::number(ms->file_index);
}
void PreviewGenerator::run() {
Q_ASSERT(footage != NULL);
Q_ASSERT(media != NULL);
Q_ASSERT(item != NULL);
QByteArray ba = media->url.toLatin1();
QByteArray ba = footage->url.toUtf8();
char* filename = new char[ba.size()+1];
strcpy(filename, ba.data());
@@ -423,48 +428,51 @@ void PreviewGenerator::run() {
error = true;
} else {
av_dump_format(fmt_ctx, 0, filename, 0);
parse_media();
sem.acquire();
parse_media();
// see if we already have data for this
QFileInfo file_info(media->url);
QString cache_file = media->url + QString::number(file_info.lastModified().toMSecsSinceEpoch());
QString hash = QCryptographicHash::hash(cache_file.toLatin1(), QCryptographicHash::Md5).toHex();
QFileInfo file_info(footage->url);
QString cache_file = footage->url.mid(footage->url.lastIndexOf('/')+1) + QString::number(file_info.size()) + QString::number(file_info.lastModified().toMSecsSinceEpoch());
//dout << "using hash" << cache_file;
QString hash = QCryptographicHash::hash(cache_file.toUtf8(), QCryptographicHash::Md5).toHex();
if (retrieve_preview(hash)) {
sem.acquire();
generate_waveform();
// save preview to file
for (int i=0;i<media->video_tracks.size();i++) {
MediaStream* ms = media->video_tracks.at(i);
for (int i=0;i<footage->video_tracks.size();i++) {
FootageStream* ms = footage->video_tracks.at(i);
ms->video_preview.save(get_thumbnail_path(hash, ms), "PNG");
//dout << "saved" << ms->file_index << "thumbnail to" << get_thumbnail_path(hash, ms);
}
for (int i=0;i<media->audio_tracks.size();i++) {
MediaStream* ms = media->audio_tracks.at(i);
for (int i=0;i<footage->audio_tracks.size();i++) {
FootageStream* ms = footage->audio_tracks.at(i);
QFile f(get_waveform_path(hash, ms));
f.open(QFile::WriteOnly);
f.write(ms->audio_preview.constData(), ms->audio_preview.size());
f.close();
//dout << "saved" << ms->file_index << "waveform to" << get_waveform_path(hash, ms);
//dout << "saved" << ms->file_index << "waveform to" << get_waveform_path(hash, ms);
}
}
sem.release();
sem.release();
}
}
avformat_close_input(&fmt_ctx);
}
if (error) {
update_footage_tooltip(item, media, errorStr);
if (error) {
media->update_tooltip(errorStr);
emit set_icon(ICON_TYPE_ERROR, replace);
media->invalid = true;
media->ready_lock.unlock();
footage->invalid = true;
footage->ready_lock.unlock();
} else {
update_footage_tooltip(item, media);
media->update_tooltip();
}
delete [] filename;
media->preview_gen = NULL;
footage->preview_gen = NULL;
}
void PreviewGenerator::cancel() {
+8 -7
View File
@@ -2,22 +2,23 @@
#define PREVIEWGENERATOR_H
#include <QThread>
#include <QSemaphore>
#define ICON_TYPE_VIDEO 0
#define ICON_TYPE_AUDIO 1
#define ICON_TYPE_IMAGE 2
#define ICON_TYPE_ERROR 3
struct Media;
struct MediaStream;
struct Footage;
struct FootageStream;
struct AVFormatContext;
class QTreeWidgetItem;
class Media;
class PreviewGenerator : public QThread
{
Q_OBJECT
public:
PreviewGenerator(QTreeWidgetItem*, Media*, bool);
PreviewGenerator(Media*, Footage*, bool);
void run();
void cancel();
signals:
@@ -28,15 +29,15 @@ private:
void generate_waveform();
void finalize_media();
AVFormatContext* fmt_ctx;
QTreeWidgetItem* item;
Media* media;
Footage* footage;
bool retrieve_duration;
bool contains_still_image;
bool replace;
bool cancelled;
QString data_path;
QString get_thumbnail_path(const QString &hash, MediaStream* ms);
QString get_waveform_path(const QString& hash, MediaStream* ms);
QString get_thumbnail_path(const QString &hash, FootageStream* ms);
QString get_waveform_path(const QString& hash, FootageStream* ms);
};
#endif // PREVIEWGENERATOR_H
+76 -41
View File
@@ -3,11 +3,12 @@
#include "io/config.h"
#include "io/path.h"
#include "io/media.h"
#include "project/footage.h"
#include "project/sequence.h"
#include "project/clip.h"
#include "project/undo.h"
#include "project/media.h"
#include "ui/sourcetable.h"
#include "ui/viewerwidget.h"
@@ -212,18 +213,23 @@ MainWindow::MainWindow(QWidget *parent) :
config_dir = data_dir + "/config.xml";
config.load(config_dir);
}
}
}
init_audio();
connect(ui->action_Undo, SIGNAL(triggered(bool)), this, SLOT(undo()));
connect(ui->action_Redo, SIGNAL(triggered(bool)), this, SLOT(redo()));
connect(ui->actionCu_t, SIGNAL(triggered(bool)), this, SLOT(cut()));
connect(ui->actionCop_y, SIGNAL(triggered(bool)), this, SLOT(copy()));
connect(ui->action_Paste, SIGNAL(triggered(bool)), this, SLOT(paste()));
connect(ui->actionProject, SIGNAL(triggered(bool)), this, SLOT(new_project()));
connect(ui->actionFull_Screen, SIGNAL(triggered(bool)), this, SLOT(toggle_full_screen()));
}
MainWindow::~MainWindow() {
panel_sequence_viewer->viewer_widget->delete_function();
panel_footage_viewer->viewer_widget->delete_function();
panel_effect_controls->clear_effects(true);
panel_sequence_viewer->viewer_widget->delete_function();
panel_footage_viewer->viewer_widget->delete_function();
set_sequence(NULL);
@@ -252,10 +258,15 @@ MainWindow::~MainWindow() {
delete ui;
delete panel_sequence_viewer;
panel_sequence_viewer = NULL;
delete panel_footage_viewer;
panel_footage_viewer = NULL;
delete panel_project;
panel_project = NULL;
delete panel_effect_controls;
panel_effect_controls = NULL;
delete panel_timeline;
panel_timeline = NULL;
close_debug();
}
@@ -305,26 +316,33 @@ void MainWindow::on_actionSequence_triggered()
nsd.exec();
}
void MainWindow::on_actionZoom_In_triggered()
{
if (panel_timeline->focused()) {
void MainWindow::on_actionZoom_In_triggered() {
QDockWidget* focused_panel = get_focused_panel();
if (focused_panel == panel_timeline) {
panel_timeline->set_zoom(true);
} else if (panel_effect_controls->keyframe_focus()) {
} else if (focused_panel == panel_effect_controls) {
panel_effect_controls->set_zoom(true);
}
} else if (focused_panel == panel_footage_viewer) {
panel_footage_viewer->set_zoom(true);
} else if (focused_panel == panel_sequence_viewer) {
panel_sequence_viewer->set_zoom(true);
}
}
void MainWindow::on_actionZoom_out_triggered()
{
if (panel_timeline->focused()) {
void MainWindow::on_actionZoom_out_triggered() {
QDockWidget* focused_panel = get_focused_panel();
if (focused_panel == panel_timeline) {
panel_timeline->set_zoom(false);
} else if (panel_effect_controls->keyframe_focus()) {
} else if (focused_panel == panel_effect_controls) {
panel_effect_controls->set_zoom(false);
}
} else if (focused_panel == panel_footage_viewer) {
panel_footage_viewer->set_zoom(false);
} else if (focused_panel == panel_sequence_viewer) {
panel_sequence_viewer->set_zoom(false);
}
}
void MainWindow::on_actionExport_triggered()
{
void MainWindow::on_actionExport_triggered() {
if (sequence == NULL) {
QMessageBox::information(this, "No active sequence", "Please open the sequence you wish to export.", QMessageBox::Ok);
} else {
@@ -386,9 +404,10 @@ void MainWindow::openSpeedDialog() {
void MainWindow::cut() {
if (sequence != NULL) {
if (panel_timeline->focused()) {
QDockWidget* focused_panel = get_focused_panel();
if (panel_timeline == focused_panel) {
panel_timeline->copy(true);
} else if (panel_effect_controls->is_focused()) {
} else if (panel_effect_controls == focused_panel) {
panel_effect_controls->copy(true);
}
}
@@ -396,22 +415,35 @@ void MainWindow::cut() {
void MainWindow::copy() {
if (sequence != NULL) {
if (panel_timeline->focused()) {
QDockWidget* focused_panel = get_focused_panel();
if (panel_timeline == focused_panel) {
panel_timeline->copy(false);
} else if (panel_effect_controls->is_focused()) {
} else if (panel_effect_controls == focused_panel) {
panel_effect_controls->copy(false);
}
}
}
void MainWindow::paste() {
if ((panel_timeline->focused() || panel_effect_controls->is_focused()) && sequence != NULL) {
QDockWidget* focused_panel = get_focused_panel();
if ((panel_timeline == focused_panel || panel_effect_controls == focused_panel) && sequence != NULL) {
panel_timeline->paste(false);
}
}
}
void MainWindow::on_actionSplit_at_Playhead_triggered()
{
void MainWindow::new_project() {
if (can_close_project()) {
panel_effect_controls->clear_effects(true);
undo_stack.clear();
project_url.clear();
panel_project->new_project();
updateTitle("");
update_ui(false);
panel_project->source_table->update();
}
}
void MainWindow::on_actionSplit_at_Playhead_triggered() {
if (panel_timeline->focused()) {
panel_timeline->split_at_playhead();
}
@@ -505,18 +537,6 @@ void MainWindow::on_action_Open_Project_triggered()
}
}
void MainWindow::on_actionProject_triggered()
{
if (can_close_project()) {
panel_effect_controls->clear_effects(true);
undo_stack.clear();
project_url.clear();
panel_project->new_project();
updateTitle("");
update_ui(false);
}
}
void MainWindow::on_actionSave_Project_As_triggered()
{
save_project_as();
@@ -582,7 +602,7 @@ void MainWindow::on_actionEdit_Tool_triggered()
void MainWindow::on_actionToggle_Snapping_triggered()
{
if (panel_timeline->focused()) panel_timeline->ui->snappingButton->click();
if (panel_timeline->focused() || panel_effect_controls->keyframe_focus()) panel_timeline->ui->snappingButton->click();
}
void MainWindow::on_actionPointer_Tool_triggered()
@@ -661,6 +681,8 @@ void MainWindow::viewMenu_About_To_Be_Shown() {
ui->action4_3->setChecked(config.show_title_safe_area && config.use_custom_title_safe_ratio && config.custom_title_safe_ratio == 4.0/3.0);
ui->action16_9->setChecked(config.show_title_safe_area && config.use_custom_title_safe_ratio && config.custom_title_safe_ratio == 16.0/9.0);
ui->actionCustom->setChecked(config.show_title_safe_area && config.use_custom_title_safe_ratio && !ui->action4_3->isChecked() && !ui->action16_9->isChecked());
ui->actionFull_Screen->setChecked(windowState() == Qt::WindowFullScreen);
}
void MainWindow::on_actionFrames_triggered()
@@ -694,6 +716,7 @@ void MainWindow::toolMenu_About_To_Be_Shown() {
ui->actionEnable_Seek_to_Import->setChecked(config.enable_seek_to_import);
ui->actionAudio_Scrubbing->setChecked(config.enable_audio_scrubbing);
ui->actionEnable_Drop_on_Media_to_Replace->setChecked(config.drop_on_media_to_replace);
ui->actionEnable_Hover_Focus->setChecked(config.hover_focus);
ui->actionNo_autoscroll->setChecked(config.autoscroll == AUTOSCROLL_NO_SCROLL);
ui->actionPage_Autoscroll->setChecked(config.autoscroll == AUTOSCROLL_PAGE_SCROLL);
@@ -809,6 +832,15 @@ void MainWindow::on_actionClear_In_Out_triggered() {
}
}
void MainWindow::toggle_full_screen() {
if (windowState() == Qt::WindowFullScreen) {
setWindowState(Qt::WindowNoState); // seems to be necessary for it to return to Maximized correctly on Linux
setWindowState(Qt::WindowMaximized);
} else {
setWindowState(Qt::WindowFullScreen);
}
}
void MainWindow::on_actionDelete_In_Out_triggered()
{
if (panel_timeline->focused()) {
@@ -977,12 +1009,11 @@ void MainWindow::on_actionNest_triggered() {
}
// add sequence to project
panel_project->new_sequence(ca, s, false, NULL);
Media* m = panel_project->new_sequence(ca, s, false, NULL);
// add nested sequence to active sequence
QVector<void*> media_list = {s};
QVector<int> type_list = {MEDIA_TYPE_SEQUENCE};
panel_timeline->create_ghosts_from_media(sequence, earliest_point, media_list, type_list);
QVector<Media*> media_list = {m};
panel_timeline->create_ghosts_from_media(sequence, earliest_point, media_list);
panel_timeline->add_clips_from_ghosts(ca, sequence);
undo_stack.push(ca);
@@ -1026,3 +1057,7 @@ void MainWindow::on_actionMilliseconds_triggered() {
config.timecode_view = TIMECODE_MILLISECONDS;
update_ui(false);
}
void MainWindow::on_actionEnable_Hover_Focus_triggered() {
config.hover_focus = !config.hover_focus;
}
+5 -4
View File
@@ -28,8 +28,11 @@ public slots:
void cut();
void copy();
void paste();
void new_project();
void autorecover_interval();
void on_actionNest_triggered();
void on_actionClear_In_Out_triggered();
void toggle_full_screen();
protected:
void closeEvent(QCloseEvent *);
@@ -70,8 +73,6 @@ private slots:
void on_action_Open_Project_triggered();
void on_actionProject_triggered();
void on_actionSave_Project_As_triggered();
void on_actionDeselect_All_triggered();
@@ -158,8 +159,6 @@ private slots:
void on_actionSet_Out_Point_triggered();
void on_actionClear_In_Out_triggered();
void on_actionDelete_In_Out_triggered();
void on_actionRipple_Delete_In_Out_triggered();
@@ -212,6 +211,8 @@ private slots:
void on_actionMilliseconds_triggered();
void on_actionEnable_Hover_Focus_triggered();
private:
Ui::MainWindow *ui;
void setup_layout(bool reset);
+22
View File
@@ -147,6 +147,8 @@
<addaction name="actionMilliseconds"/>
<addaction name="separator"/>
<addaction name="menuTitle_Action_Safe_Area"/>
<addaction name="separator"/>
<addaction name="actionFull_Screen"/>
</widget>
<widget class="QMenu" name="menuPlayback">
<property name="title">
@@ -187,6 +189,7 @@
<addaction name="actionEnable_Seek_to_Import"/>
<addaction name="actionAudio_Scrubbing"/>
<addaction name="actionEnable_Drop_on_Media_to_Replace"/>
<addaction name="actionEnable_Hover_Focus"/>
<addaction name="separator"/>
<addaction name="actionNo_autoscroll"/>
<addaction name="actionPage_Autoscroll"/>
@@ -925,6 +928,25 @@
<string>Milliseconds</string>
</property>
</action>
<action name="actionEnable_Hover_Focus">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Enable Hover Focus for Zooming</string>
</property>
</action>
<action name="actionFull_Screen">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Full Screen</string>
</property>
<property name="shortcut">
<string>F11</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
+205 -196
View File
@@ -1,196 +1,205 @@
#-------------------------------------------------
#
# Project created by QtCreator 2018-05-11T10:31:59
#
#-------------------------------------------------
QT += core gui multimedia opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = Olive
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp \
panels/project.cpp \
panels/effectcontrols.cpp \
panels/viewer.cpp \
panels/timeline.cpp \
ui/sourcetable.cpp \
dialogs/aboutdialog.cpp \
ui/timelinewidget.cpp \
io/media.cpp \
project/sequence.cpp \
project/clip.cpp \
playback/playback.cpp \
playback/audio.cpp \
io/config.cpp \
dialogs/newsequencedialog.cpp \
ui/viewerwidget.cpp \
ui/viewercontainer.cpp \
dialogs/exportdialog.cpp \
ui/collapsiblewidget.cpp \
panels/panels.cpp \
playback/cacher.cpp \
io/exportthread.cpp \
ui/timelineheader.cpp \
io/previewgenerator.cpp \
ui/labelslider.cpp \
dialogs/preferencesdialog.cpp \
ui/audiomonitor.cpp \
project/undo.cpp \
ui/scrollarea.cpp \
ui/comboboxex.cpp \
ui/colorbutton.cpp \
dialogs/replaceclipmediadialog.cpp \
ui/fontcombobox.cpp \
ui/checkboxex.cpp \
ui/keyframeview.cpp \
ui/texteditex.cpp \
dialogs/demonotice.cpp \
project/marker.cpp \
dialogs/speeddialog.cpp \
dialogs/mediapropertiesdialog.cpp \
io/crc32.cpp \
dialogs/loaddialog.cpp \
debug.cpp \
io/path.cpp \
effects/internal/linearfadetransition.cpp \
effects/internal/transformeffect.cpp \
effects/internal/solideffect.cpp \
effects/internal/texteffect.cpp \
effects/internal/timecodeeffect.cpp \
effects/internal/audionoiseeffect.cpp \
effects/internal/paneffect.cpp \
effects/internal/toneeffect.cpp \
effects/internal/volumeeffect.cpp \
effects/internal/crossdissolvetransition.cpp \
effects/internal/shakeeffect.cpp \
effects/internal/exponentialfadetransition.cpp \
effects/internal/logarithmicfadetransition.cpp \
effects/internal/cornerpineffect.cpp \
io/math.cpp \
io/qpainterwrapper.cpp \
project/effect.cpp \
project/transition.cpp \
project/effectrow.cpp \
project/effectfield.cpp \
effects/internal/cubetransition.cpp \
project/effectgizmo.cpp \
io/clipboard.cpp
HEADERS += \
mainwindow.h \
panels/project.h \
panels/effectcontrols.h \
panels/viewer.h \
panels/timeline.h \
ui/sourcetable.h \
dialogs/aboutdialog.h \
ui/timelinewidget.h \
io/media.h \
project/sequence.h \
project/clip.h \
playback/playback.h \
playback/audio.h \
io/config.h \
dialogs/newsequencedialog.h \
ui/viewerwidget.h \
ui/viewercontainer.h \
dialogs/exportdialog.h \
ui/collapsiblewidget.h \
panels/panels.h \
playback/cacher.h \
io/exportthread.h \
ui/timelinetools.h \
ui/timelineheader.h \
io/previewgenerator.h \
ui/labelslider.h \
dialogs/preferencesdialog.h \
ui/audiomonitor.h \
project/undo.h \
ui/scrollarea.h \
ui/comboboxex.h \
ui/colorbutton.h \
dialogs/replaceclipmediadialog.h \
ui/fontcombobox.h \
ui/checkboxex.h \
ui/keyframeview.h \
ui/texteditex.h \
dialogs/demonotice.h \
project/marker.h \
project/selection.h \
dialogs/speeddialog.h \
dialogs/mediapropertiesdialog.h \
io/crc32.h \
dialogs/loaddialog.h \
debug.h \
io/path.h \
effects/internal/transformeffect.h \
effects/internal/solideffect.h \
effects/internal/texteffect.h \
effects/internal/timecodeeffect.h \
effects/internal/audionoiseeffect.h \
effects/internal/paneffect.h \
effects/internal/toneeffect.h \
effects/internal/volumeeffect.h \
effects/internal/shakeeffect.h \
effects/internal/linearfadetransition.h \
effects/internal/crossdissolvetransition.h \
effects/internal/exponentialfadetransition.h \
effects/internal/logarithmicfadetransition.h \
effects/internal/cornerpineffect.h \
io/math.h \
io/qpainterwrapper.h \
project/effect.h \
project/transition.h \
project/effectrow.h \
project/effectfield.h \
effects/internal/cubetransition.h \
project/effectgizmo.h \
io/clipboard.h
FORMS += \
mainwindow.ui \
panels/project.ui \
panels/effectcontrols.ui \
panels/viewer.ui \
panels/timeline.ui \
dialogs/aboutdialog.ui \
dialogs/newsequencedialog.ui \
dialogs/exportdialog.ui \
dialogs/preferencesdialog.ui \
dialogs/demonotice.ui
win32 {
RC_FILE = icons/resources.rc
LIBS += -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lopengl32
}
mac {
LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample
ICON = icons/olive.icns
INCLUDEPATH = /usr/local/include
}
linux {
CONFIG += link_pkgconfig
PKGCONFIG += libavutil libavformat libavcodec libavfilter libswscale libswresample
}
RESOURCES += \
icons/icons.qrc
#-------------------------------------------------
#
# Project created by QtCreator 2018-05-11T10:31:59
#
#-------------------------------------------------
QT += core gui multimedia opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = Olive
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp \
panels/project.cpp \
panels/effectcontrols.cpp \
panels/viewer.cpp \
panels/timeline.cpp \
ui/sourcetable.cpp \
dialogs/aboutdialog.cpp \
ui/timelinewidget.cpp \
project/media.cpp \
project/footage.cpp \
project/sequence.cpp \
project/clip.cpp \
playback/playback.cpp \
playback/audio.cpp \
io/config.cpp \
dialogs/newsequencedialog.cpp \
ui/viewerwidget.cpp \
ui/viewercontainer.cpp \
dialogs/exportdialog.cpp \
ui/collapsiblewidget.cpp \
panels/panels.cpp \
playback/cacher.cpp \
io/exportthread.cpp \
ui/timelineheader.cpp \
io/previewgenerator.cpp \
ui/labelslider.cpp \
dialogs/preferencesdialog.cpp \
ui/audiomonitor.cpp \
project/undo.cpp \
ui/scrollarea.cpp \
ui/comboboxex.cpp \
ui/colorbutton.cpp \
dialogs/replaceclipmediadialog.cpp \
ui/fontcombobox.cpp \
ui/checkboxex.cpp \
ui/keyframeview.cpp \
ui/texteditex.cpp \
dialogs/demonotice.cpp \
project/marker.cpp \
dialogs/speeddialog.cpp \
dialogs/mediapropertiesdialog.cpp \
io/crc32.cpp \
project/projectmodel.cpp \
io/loadthread.cpp \
dialogs/loaddialog.cpp \
debug.cpp \
io/path.cpp \
effects/internal/linearfadetransition.cpp \
effects/internal/transformeffect.cpp \
effects/internal/solideffect.cpp \
effects/internal/texteffect.cpp \
effects/internal/timecodeeffect.cpp \
effects/internal/audionoiseeffect.cpp \
effects/internal/paneffect.cpp \
effects/internal/toneeffect.cpp \
effects/internal/volumeeffect.cpp \
effects/internal/crossdissolvetransition.cpp \
effects/internal/shakeeffect.cpp \
effects/internal/exponentialfadetransition.cpp \
effects/internal/logarithmicfadetransition.cpp \
effects/internal/cornerpineffect.cpp \
io/math.cpp \
io/qpainterwrapper.cpp \
project/effect.cpp \
project/transition.cpp \
project/effectrow.cpp \
project/effectfield.cpp \
effects/internal/cubetransition.cpp \
project/effectgizmo.cpp \
io/clipboard.cpp \
dialogs/stabilizerdialog.cpp \
io/avtogl.cpp
HEADERS += \
mainwindow.h \
panels/project.h \
panels/effectcontrols.h \
panels/viewer.h \
panels/timeline.h \
ui/sourcetable.h \
dialogs/aboutdialog.h \
ui/timelinewidget.h \
project/media.h \
project/footage.h \
project/sequence.h \
project/clip.h \
playback/playback.h \
playback/audio.h \
io/config.h \
dialogs/newsequencedialog.h \
ui/viewerwidget.h \
ui/viewercontainer.h \
dialogs/exportdialog.h \
ui/collapsiblewidget.h \
panels/panels.h \
playback/cacher.h \
io/exportthread.h \
ui/timelinetools.h \
ui/timelineheader.h \
io/previewgenerator.h \
ui/labelslider.h \
dialogs/preferencesdialog.h \
ui/audiomonitor.h \
project/undo.h \
ui/scrollarea.h \
ui/comboboxex.h \
ui/colorbutton.h \
dialogs/replaceclipmediadialog.h \
ui/fontcombobox.h \
ui/checkboxex.h \
ui/keyframeview.h \
ui/texteditex.h \
dialogs/demonotice.h \
project/marker.h \
project/selection.h \
dialogs/speeddialog.h \
dialogs/mediapropertiesdialog.h \
io/crc32.h \
project/projectmodel.h \
io/loadthread.h \
dialogs/loaddialog.h \
debug.h \
io/path.h \
effects/internal/transformeffect.h \
effects/internal/solideffect.h \
effects/internal/texteffect.h \
effects/internal/timecodeeffect.h \
effects/internal/audionoiseeffect.h \
effects/internal/paneffect.h \
effects/internal/toneeffect.h \
effects/internal/volumeeffect.h \
effects/internal/shakeeffect.h \
effects/internal/linearfadetransition.h \
effects/internal/crossdissolvetransition.h \
effects/internal/exponentialfadetransition.h \
effects/internal/logarithmicfadetransition.h \
effects/internal/cornerpineffect.h \
io/math.h \
io/qpainterwrapper.h \
project/effect.h \
project/transition.h \
project/effectrow.h \
project/effectfield.h \
effects/internal/cubetransition.h \
project/effectgizmo.h \
io/clipboard.h \
dialogs/stabilizerdialog.h \
io/avtogl.h
FORMS += \
mainwindow.ui \
panels/effectcontrols.ui \
panels/viewer.ui \
panels/timeline.ui \
dialogs/aboutdialog.ui \
dialogs/newsequencedialog.ui \
dialogs/exportdialog.ui \
dialogs/preferencesdialog.ui \
dialogs/demonotice.ui
win32 {
RC_FILE = icons/resources.rc
LIBS += -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lopengl32
}
mac {
LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample
ICON = icons/olive.icns
INCLUDEPATH = /usr/local/include
}
linux {
CONFIG += link_pkgconfig
PKGCONFIG += libavutil libavformat libavcodec libavfilter libswscale libswresample
}
RESOURCES += \
icons/icons.qrc
+2
View File
@@ -201,6 +201,8 @@ void EffectControls::show_effect_menu(int type, int subtype) {
void EffectControls::clear_effects(bool clear_cache) {
// clear existing clips
deselect_all_effects(NULL);
QVBoxLayout* video_layout = static_cast<QVBoxLayout*>(ui->video_effect_area->layout());
QVBoxLayout* audio_layout = static_cast<QVBoxLayout*>(ui->audio_effect_area->layout());
QLayoutItem* item;
+34 -1
View File
@@ -3,9 +3,11 @@
#include "timeline.h"
#include "effectcontrols.h"
#include "viewer.h"
#include "project.h"
#include "project/sequence.h"
#include "project/clip.h"
#include "project/transition.h"
#include "io/config.h"
#include "debug.h"
Project* panel_project = 0;
@@ -99,8 +101,39 @@ void update_effect_controls() {
void update_ui(bool modified) {
if (modified) {
update_effect_controls();
}
}
panel_effect_controls->update_keyframes();
panel_timeline->repaint_timeline();
panel_sequence_viewer->update_viewer();
}
QDockWidget *get_focused_panel() {
QDockWidget* w = NULL;
if (config.hover_focus) {
if (panel_project->rect().contains(panel_project->mapFromGlobal(QCursor::pos()))) {
w = panel_project;
} else if (panel_effect_controls->rect().contains(panel_effect_controls->mapFromGlobal(QCursor::pos()))) {
w = panel_effect_controls;
} else if (panel_sequence_viewer->rect().contains(panel_sequence_viewer->mapFromGlobal(QCursor::pos()))) {
w = panel_sequence_viewer;
} else if (panel_footage_viewer->rect().contains(panel_footage_viewer->mapFromGlobal(QCursor::pos()))) {
w = panel_footage_viewer;
} else if (panel_timeline->rect().contains(panel_timeline->mapFromGlobal(QCursor::pos()))) {
w = panel_timeline;
}
}
if (w == NULL) {
if (panel_project->is_focused()) {
w = panel_project;
} else if (panel_effect_controls->keyframe_focus() || panel_effect_controls->is_focused()) {
w = panel_effect_controls;
} else if (panel_sequence_viewer->is_focused()) {
w = panel_sequence_viewer;
} else if (panel_footage_viewer->is_focused()) {
w = panel_footage_viewer;
} else if (panel_timeline->focused()) {
w = panel_timeline;
}
}
return w;
}
+2
View File
@@ -5,6 +5,7 @@ class Project;
class EffectControls;
class Viewer;
class Timeline;
class QDockWidget;
extern Project* panel_project;
extern EffectControls* panel_effect_controls;
@@ -13,5 +14,6 @@ extern Viewer* panel_footage_viewer;
extern Timeline* panel_timeline;
void update_ui(bool modified);
QDockWidget* get_focused_panel();
#endif // PANELS_H
+266 -980
View File
File diff suppressed because it is too large Load Diff
+31 -45
View File
@@ -6,16 +6,19 @@
#include <QTimer>
#include <QDir>
struct Media;
#include "project/projectmodel.h"
struct Footage;
struct Sequence;
struct Clip;
class Timeline;
class Viewer;
class SourceTable;
class QTreeWidgetItem;
class Media;
class QXmlStreamWriter;
class QXmlStreamReader;
class QFile;
class QSortFilterProxyModel;
class ComboAction;
#define LOAD_TYPE_VERSION 69
@@ -30,18 +33,11 @@ extern QString project_url;
extern QStringList recent_projects;
extern QString recent_proj_file;
int get_type_from_tree(QTreeWidgetItem* item);
void* get_media_from_tree(QTreeWidgetItem* item);
Media* get_footage_from_tree(QTreeWidgetItem* item);
void set_footage_of_tree(QTreeWidgetItem* item, Media* media);
Sequence* get_sequence_from_tree(QTreeWidgetItem* item);
void set_sequence_of_tree(QTreeWidgetItem* item, Sequence* sequence);
void set_item_to_folder(QTreeWidgetItem* item);
void update_footage_tooltip(QTreeWidgetItem* item, Media* media, QString error = 0);
extern ProjectModel project_model;
Sequence* create_sequence_from_media(QVector<void*>& media_list, QVector<int>& type_list);
Sequence* create_sequence_from_media(QVector<Media *> &media_list);
QString get_channel_layout_name(int channels, int layout);
QString get_channel_layout_name(int channels, uint64_t layout);
QString get_interlacing_name(int interlacing);
class Project : public QDockWidget
@@ -53,27 +49,33 @@ public:
~Project();
bool is_focused();
void clear();
void new_sequence(ComboAction *ca, Sequence* s, bool open, QTreeWidgetItem* parent);
QString get_next_sequence_name(QString start = 0);
void delete_media(QTreeWidgetItem* item);
void process_file_list(bool recursive, QStringList& files, QTreeWidgetItem *parent, QTreeWidgetItem* replace);
void replace_media(QTreeWidgetItem* item, QString filename);
QTreeWidgetItem* get_selected_folder();
bool reveal_media(void* media, QTreeWidgetItem *parent = 0);
Media* new_sequence(ComboAction *ca, Sequence* s, bool open, Media* parent);
QString get_next_sequence_name(QString start = 0);
void process_file_list(QStringList& files, bool recursive = false, Media* replace = NULL, Media *parent = NULL);
void replace_media(Media* item, QString filename);
Media *get_selected_folder();
bool reveal_media(void* media, QModelIndex parent = QModelIndex());
void add_recent_project(QString url);
void new_project();
void load_project(bool autorecovery);
void save_project(bool autorecovery);
QTreeWidgetItem* new_folder(QString name);
Media* new_folder(QString name);
Media* item_to_media(const QModelIndex& index);
void save_recent_projects();
QVector<Sequence*> list_all_project_sequences();
QVector<Media*> list_all_project_sequences();
SourceTable* source_table;
QSortFilterProxyModel* sorter;
QVector<Media*> last_imported_media;
QVector<Media*> last_imported_media;
//Media *new_item();
void start_preview_generator(Media* item, bool replacing);
public slots:
void import_dialog();
void delete_selected_media();
@@ -83,48 +85,32 @@ public slots:
void replace_clip_media();
void open_properties();
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, bool set_ids_only);
bool show_err;
QString error_str;
void save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, const QModelIndex &parent = QModelIndex());
int folder_id;
int media_id;
int sequence_id;
Sequence* open_seq;
QVector<QTreeWidgetItem*> loaded_folders;
QVector<Media*> loaded_media;
QVector<QTreeWidgetItem*> loaded_media_items;
QVector<Clip*> loaded_clips;
QVector<Sequence*> loaded_sequences;
QTreeWidgetItem* find_loaded_folder_by_id(int id);
void add_recent_project(QString url);
void get_all_media_from_table(QList<QTreeWidgetItem*> items, QList<QTreeWidgetItem*>& list, int type);
void start_preview_generator(QTreeWidgetItem* item, Media* media, bool replacing);
void list_all_sequences_worker(QVector<Sequence*>* list, QTreeWidgetItem* parent);
void get_all_media_from_table(QList<Media *> items, QList<Media *> &list, int type);
void list_all_sequences_worker(QVector<Media *> *list, Media* parent);
QString get_file_name_from_path(const QString &path);
QDir proj_dir;
QDir internal_proj_dir;
QString internal_proj_url;
private slots:
void rename_media(QTreeWidgetItem* item, int column);
void clear_recent_projects();
};
class MediaThrobber : public QObject {
Q_OBJECT
public:
MediaThrobber(QTreeWidgetItem*);
MediaThrobber(Media*);
public slots:
void start();
void stop(int, bool replace);
private slots:
void animation_update();
private:
QPixmap pixmap;
int animation;
QTreeWidgetItem* item;
QTimer animator;
Media* item;
QTimer* animator;
};
#endif // PROJECT_H
-100
View File
@@ -1,100 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Project</class>
<widget class="QDockWidget" name="Project">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>504</width>
<height>371</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Project</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="SourceTable" name="treeWidget">
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="dragEnabled">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="rootIsDecorated">
<bool>true</bool>
</property>
<property name="itemsExpandable">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>false</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
<attribute name="headerStretchLastSection">
<bool>true</bool>
</attribute>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Duration</string>
</property>
</column>
<column>
<property name="text">
<string>Rate</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>SourceTable</class>
<extends>QTreeWidget</extends>
<header>ui/sourcetable.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+75 -33
View File
@@ -14,10 +14,11 @@
#include "playback/playback.h"
#include "ui_viewer.h"
#include "project/undo.h"
#include "project/media.h"
#include "io/config.h"
#include "project/effect.h"
#include "project/transition.h"
#include "io/media.h"
#include "project/footage.h"
#include "io/clipboard.h"
#include "debug.h"
@@ -30,6 +31,7 @@
#include <QMenu>
#include <QInputDialog>
#include <QMessageBox>
#include <QCheckBox>
long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) {
return qRound(((double)framenumber/source_frame_rate)*target_frame_rate);
@@ -160,23 +162,24 @@ void Timeline::toggle_show_all() {
}
}
void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector<void*>& media_list, QVector<int>& type_list) {
void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector<Media*>& media_list) {
video_ghosts = false;
audio_ghosts = false;
for (int i=0;i<media_list.size();i++) {
bool can_import = true;
Media* m = NULL;
Sequence* s = NULL;
void* media = NULL;
Media* medium = media_list.at(i);
Footage* m = NULL;
Sequence* s = NULL;
void* media = NULL;
long sequence_length;
long default_clip_in = 0;
long default_clip_out = 0;
switch (type_list.at(i)) {
switch (medium->get_type()) {
case MEDIA_TYPE_FOOTAGE:
m = static_cast<Media*>(media_list.at(i));
m = medium->to_footage();
media = m;
can_import = m->ready;
if (m->using_inout) {
@@ -187,7 +190,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector
}
break;
case MEDIA_TYPE_SEQUENCE:
s = static_cast<Sequence*>(media_list.at(i));
s = medium->to_sequence();
sequence_length = s->getEndFrame();
if (seq != NULL) sequence_length = refactor_frame_number(sequence_length, s->frame_rate, seq->frame_rate);
media = s;
@@ -203,15 +206,14 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector
if (can_import) {
Ghost g;
g.clip = -1;
g.media_type = type_list.at(i);
g.clip = -1;
g.trimming = false;
g.old_clip_in = g.clip_in = default_clip_in;
g.media = media;
g.media = medium;
g.in = entry_point;
g.transition = NULL;
switch (type_list.at(i)) {
switch (medium->get_type()) {
case MEDIA_TYPE_FOOTAGE:
// is video source a still image?
if (m->video_tracks.size() > 0 && m->video_tracks[0]->infinite_length && m->audio_tracks.size() == 0) {
@@ -274,15 +276,14 @@ void Timeline::add_clips_from_ghosts(ComboAction* ca, Sequence* s) {
earliest_point = qMin(earliest_point, g.in);
Clip* c = new Clip(s);
c->media = g.media;
c->media_type = g.media_type;
c->media = g.media;
c->media_stream = g.media_stream;
c->timeline_in = g.in;
c->timeline_out = g.out;
c->clip_in = g.clip_in;
c->track = g.track;
if (c->media_type == MEDIA_TYPE_FOOTAGE) {
Media* m = static_cast<Media*>(c->media);
if (c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
Footage* m = c->media->to_footage();
if (m->video_tracks.size() == 0) {
// audio only (greenish)
c->color_r = 128;
@@ -300,13 +301,13 @@ void Timeline::add_clips_from_ghosts(ComboAction* ca, Sequence* s) {
c->color_b = 192;
}
c->name = m->name;
} else if (c->media_type == MEDIA_TYPE_SEQUENCE) {
} else if (c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
// sequence (red?ish?)
c->color_r = 192;
c->color_g = 128;
c->color_b = 128;
Sequence* media = static_cast<Sequence*>(c->media);
Sequence* media = c->media->to_sequence();
c->name = media->name;
}
c->recalculateMaxLength();
@@ -417,16 +418,6 @@ bool Timeline::focused() {
return (sequence != NULL && (ui->headers->hasFocus() || ui->video_area->hasFocus() || ui->audio_area->hasFocus()));
}
bool Timeline::center_scroll_to_playhead() {
// returns true is the scroll was changed, false if not
int target_scroll = qMin(ui->horizontalScrollBar->maximum(), qMax(0, getScreenPointFromFrame(zoom, sequence->playhead)-(ui->editAreas->width()>>1)));
if (target_scroll == ui->horizontalScrollBar->value()) {
return false;
}
ui->horizontalScrollBar->setValue(target_scroll);
return true;
}
void Timeline::repaint_timeline() {
bool draw = true;
@@ -442,7 +433,7 @@ void Timeline::repaint_timeline() {
draw = false;
}
} else if (config.autoscroll == AUTOSCROLL_SMOOTH_SCROLL) {
if (center_scroll_to_playhead()) {
if (center_scroll_to_playhead(ui->horizontalScrollBar, zoom, sequence->playhead)) {
draw = false;
}
}
@@ -458,7 +449,7 @@ void Timeline::repaint_timeline() {
if (sequence != NULL) {
long sequenceEndFrame = sequence->getEndFrame();
ui->horizontalScrollBar->setMaximum(qMax(0, getScreenPointFromFrame(zoom, sequenceEndFrame) - (ui->editAreas->width()/2)));
ui->headers->set_scrollbar_max(ui->horizontalScrollBar, sequenceEndFrame, (ui->editAreas->width()/2));
if (last_frame != sequence->playhead) {
ui->audio_monitor->update();
@@ -579,7 +570,7 @@ void Timeline::set_zoom_value(double v) {
repaint_timeline();
// TODO find a way to gradually move towards target_scroll instead of just centering it?
center_scroll_to_playhead();
center_scroll_to_playhead(ui->horizontalScrollBar, zoom, sequence->playhead);
}
void Timeline::set_zoom(bool in) {
@@ -975,14 +966,65 @@ void Timeline::paste(bool insert) {
} else if (clipboard_type == CLIPBOARD_TYPE_EFFECT) {
ComboAction* ca = new ComboAction();
bool push = false;
bool replace = false;
bool skip = false;
bool ask_conflict = true;
for (int i=0;i<sequence->clips.size();i++) {
Clip* c = sequence->clips.at(i);
if (c != NULL && is_clip_selected(c, true)) {
for (int j=0;j<clipboard.size();j++) {
Effect* e = static_cast<Effect*>(clipboard.at(j));
if ((c->track < 0) == (e->meta->subtype == EFFECT_TYPE_VIDEO)) {
ca->append(new AddEffectCommand(c, e->copy(c), NULL));
push = true;
int found = -1;
if (ask_conflict) {
replace = false;
skip = false;
}
for (int k=0;k<c->effects.size();k++) {
if (c->effects.at(k)->meta == e->meta) {
found = k;
break;
}
}
if (found >= 0 && ask_conflict) {
QMessageBox box(this);
box.setWindowTitle("Effect already exists");
box.setText("Clip '" + c->name + "' already contains a '" + e->meta->name + "' effect. Would you like to replace it with the pasted one or add it as a separate effect?");
box.setIcon(QMessageBox::Icon::Question);
box.addButton("Add", QMessageBox::YesRole);
QPushButton* replace_button = box.addButton("Replace", QMessageBox::NoRole);
QPushButton* skip_button = box.addButton("Skip", QMessageBox::RejectRole);
QCheckBox* future_box = new QCheckBox("Do this for all conflicts found");
box.setCheckBox(future_box);
box.exec();
if (box.clickedButton() == replace_button) {
replace = true;
} else if (box.clickedButton() == skip_button) {
skip = true;
}
ask_conflict = !future_box->isChecked();
}
if (found >= 0 && skip) {
// do nothing
} else if (found >= 0 && replace) {
EffectDeleteCommand* delcom = new EffectDeleteCommand();
delcom->clips.append(c);
delcom->fx.append(found);
ca->append(delcom);
ca->append(new AddEffectCommand(c, e->copy(c), NULL, found));
push = true;
} else {
ca->append(new AddEffectCommand(c, e->copy(c), NULL));
push = true;
}
}
}
}
+6 -7
View File
@@ -22,12 +22,13 @@ class SourceTable;
class ViewerWidget;
class ComboAction;
class Effect;
class Media;
class Transition;
struct EffectMeta;
struct Sequence;
struct Clip;
struct Media;
struct MediaStream;
struct Footage;
struct FootageStream;
long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate);
int getScreenPointFromFrame(double zoom, long frame);
@@ -49,8 +50,7 @@ struct Ghost {
long old_clip_in;
// importing variables
void* media;
int media_type;
Media* media;
int media_stream;
// other variables
@@ -102,7 +102,7 @@ public:
void next_cut();
void toggle_show_all();
void create_ghosts_from_media(Sequence *seq, long entry_point, QVector<void *> &media_list, QVector<int> &type_list);
void create_ghosts_from_media(Sequence *seq, long entry_point, QVector<Media *> &media_list);
void add_clips_from_ghosts(ComboAction *ca, Sequence *s);
int getTimelineScreenPointFromFrame(long frame);
@@ -227,8 +227,7 @@ private:
void set_zoom_value(double v);
QVector<QPushButton*> tool_buttons;
void decheck_tool_buttons(QObject* sender);
void set_tool(int tool);
bool center_scroll_to_playhead();
void set_tool(int tool);
long last_frame;
int scroll;
+130 -102
View File
@@ -8,11 +8,13 @@
#include "project/clip.h"
#include "panels/panels.h"
#include "io/config.h"
#include "io/media.h"
#include "project/footage.h"
#include "project/media.h"
#include "project/undo.h"
#include "ui/audiomonitor.h"
#include "ui_timeline.h"
#include "playback/playback.h"
#include "ui/viewerwidget.h"
#include "debug.h"
#define FRAMES_IN_ONE_MINUTE 1798 // 1800 - 2
@@ -33,20 +35,22 @@ Viewer::Viewer(QWidget *parent) :
QDockWidget(parent),
playing(false),
just_played(false),
media(NULL),
seq(NULL),
ui(new Ui::Viewer),
created_sequence(false),
cue_recording_internal(false),
panel_name("Viewer: ")
panel_name("Viewer: "),
minimum_zoom(1.0)
{
ui->setupUi(this);
ui->headers->viewer = this;
ui->headers->snapping = false;
ui->headers->show_text(false);
ui->glViewerPane->child = ui->openGLWidget;
ui->openGLWidget->viewer = this;
viewer_widget = ui->openGLWidget;
set_media(MEDIA_TYPE_SEQUENCE, NULL);
ui->glViewerPane->viewer = this;
viewer_widget = ui->glViewerPane->child;
viewer_widget->viewer = this;
set_media(NULL);
ui->currentTimecode->setEnabled(false);
ui->currentTimecode->set_minimum_value(0);
@@ -59,6 +63,9 @@ Viewer::Viewer(QWidget *parent) :
connect(&playback_updater, SIGNAL(timeout()), this, SLOT(timer_update()));
connect(&recording_flasher, SIGNAL(timeout()), this, SLOT(recording_flasher_update()));
connect(ui->horizontalScrollBar, SIGNAL(valueChanged(int)), ui->headers, SLOT(set_scroll(int)));
connect(ui->horizontalScrollBar, SIGNAL(valueChanged(int)), viewer_widget, SLOT(set_waveform_scroll(int)));
connect(ui->zoomComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(zoom_update(int)));
update_playhead_timecode(0);
update_end_timecode();
@@ -70,7 +77,7 @@ Viewer::~Viewer() {
bool Viewer::is_focused() {
return ui->headers->hasFocus()
|| ui->openGLWidget->hasFocus()
|| viewer_widget->hasFocus()
|| ui->pushButton->hasFocus()
|| ui->pushButton_2->hasFocus()
|| ui->pushButton_3->hasFocus()
@@ -97,15 +104,6 @@ void Viewer::reset_all_audio() {
clear_audio_ibuffer();
}
void Viewer::assert_audio_device() {
if (is_audio_device_set() &&
(audio_output->format().sampleRate() != seq->audio_frequency
|| audio_output->format().channelCount() != av_get_channel_layout_nb_channels(seq->audio_layout))) {
closeActiveClips(seq, true);
init_audio(seq);
}
}
long timecode_to_frame(const QString& s, int view, double frame_rate) {
QList<QString> list = s.split(QRegExp("[:;]"));
@@ -237,7 +235,6 @@ void Viewer::seek(long p) {
seq->playhead = p;
update_parents();
reset_all_audio();
assert_audio_device();
audio_scrub = true;
}
@@ -302,7 +299,6 @@ void Viewer::play() {
if (seq != NULL) {
reset_all_audio();
assert_audio_device();
if (is_recording_cued() && !start_recording()) {
dout << "[ERROR] Failed to record audio";
return;
@@ -340,27 +336,27 @@ void Viewer::pause() {
// import audio
QStringList file_list;
file_list.append(get_recorded_audio_filename());
panel_project->process_file_list(false, file_list, NULL, NULL);
panel_project->process_file_list(file_list);
// add it to the sequence
Clip* c = new Clip(seq);
Media* m = panel_project->last_imported_media.at(0);
Media* m = panel_project->last_imported_media.at(0);
Footage* f = m->to_footage();
m->ready_lock.lock();
f->ready_lock.lock();
c->media = m; // latest media
c->media_type = MEDIA_TYPE_FOOTAGE;
c->media = m; // latest media
c->media_stream = 0;
c->timeline_in = recording_start;
c->timeline_out = m->get_length_in_frames(seq->frame_rate);
c->timeline_out = f->get_length_in_frames(seq->frame_rate);
c->clip_in = 0;
c->track = recording_track;
c->color_r = 128;
c->color_g = 192;
c->color_b = 128;
c->name = m->name;
c->name = m->get_name();
m->ready_lock.unlock();
f->ready_lock.unlock();
QVector<Clip*> add_clips;
add_clips.append(c);
@@ -380,7 +376,14 @@ void Viewer::update_end_timecode() {
void Viewer::update_header_zoom() {
if (seq != NULL) {
long sequenceEndFrame = seq->getEndFrame();
ui->headers->update_zoom((sequenceEndFrame > 0) ? ((double) ui->headers->width() / (double) sequenceEndFrame) : 1);
if (cached_end_frame != sequenceEndFrame) {
minimum_zoom = (sequenceEndFrame > 0) ? ((double) ui->headers->width() / (double) sequenceEndFrame) : 1;
ui->headers->update_zoom(qMax(ui->headers->get_zoom(), minimum_zoom));
ui->headers->set_scrollbar_max(ui->horizontalScrollBar, sequenceEndFrame, ui->headers->width());
viewer_widget->waveform_zoom = ui->headers->get_zoom();
} else {
ui->headers->update();
}
}
}
@@ -393,8 +396,8 @@ void Viewer::update_parents() {
}
void Viewer::update_viewer() {
update_header_zoom();
viewer_widget->update();
update_header_zoom();
if (seq != NULL) update_playhead_timecode(seq->playhead);
update_end_timecode();
}
@@ -411,86 +414,102 @@ void Viewer::set_in_point() {
}
void Viewer::set_out_point() {
ui->headers->set_out_point(seq->playhead);
ui->headers->set_out_point(seq->playhead);
}
void Viewer::set_media(int type, void* media) {
void Viewer::set_zoom(bool in) {
if (seq != NULL) {
if (in) {
ui->headers->update_zoom(ui->headers->get_zoom()*2);
} else {
ui->headers->update_zoom(qMax(minimum_zoom, ui->headers->get_zoom()*0.5));
}
if (viewer_widget->waveform) {
viewer_widget->waveform_zoom = ui->headers->get_zoom();
viewer_widget->update();
}
ui->headers->set_scrollbar_max(ui->horizontalScrollBar, seq->getEndFrame(), ui->headers->width());
center_scroll_to_playhead(ui->horizontalScrollBar, ui->headers->get_zoom(), seq->playhead);
}
}
void Viewer::set_media(Media* m) {
main_sequence = false;
clean_created_seq();
switch (type) {
case MEDIA_TYPE_FOOTAGE:
{
Media* footage = static_cast<Media*>(media);
media = m;
clean_created_seq();
if (media != NULL) {
switch (media->get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
Footage* footage = media->to_footage();
seq = new Sequence();
created_sequence = true;
seq->wrapper_sequence = true;
seq->name = footage->name;
seq = new Sequence();
created_sequence = true;
seq->wrapper_sequence = true;
seq->name = footage->name;
seq->using_workarea = footage->using_inout;
if (footage->using_inout) {
seq->workarea_in = footage->in;
seq->workarea_out = footage->out;
}
seq->using_workarea = footage->using_inout;
if (footage->using_inout) {
seq->workarea_in = footage->in;
seq->workarea_out = footage->out;
}
seq->frame_rate = 30;
seq->frame_rate = 30;
if (footage->video_tracks.size() > 0) {
MediaStream* video_stream = footage->video_tracks.at(0);
seq->width = video_stream->video_width;
seq->height = video_stream->video_height;
if (video_stream->video_frame_rate > 0 && !video_stream->infinite_length) seq->frame_rate = video_stream->video_frame_rate;
if (footage->video_tracks.size() > 0) {
FootageStream* video_stream = footage->video_tracks.at(0);
seq->width = video_stream->video_width;
seq->height = video_stream->video_height;
if (video_stream->video_frame_rate > 0 && !video_stream->infinite_length) seq->frame_rate = video_stream->video_frame_rate;
Clip* c = new Clip(seq);
c->media = footage;
c->media_type = type;
c->media_stream = video_stream->file_index;
c->timeline_in = 0;
c->timeline_out = footage->get_length_in_frames(seq->frame_rate);
if (c->timeline_out <= 0) c->timeline_out = 150;
c->track = -1;
c->clip_in = 0;
c->recalculateMaxLength();
seq->clips.append(c);
} else {
seq->width = 1920;
seq->height = 1080;
}
Clip* c = new Clip(seq);
c->media = media;
c->media_stream = video_stream->file_index;
c->timeline_in = 0;
c->timeline_out = footage->get_length_in_frames(seq->frame_rate);
if (c->timeline_out <= 0) c->timeline_out = 150;
c->track = -1;
c->clip_in = 0;
c->recalculateMaxLength();
seq->clips.append(c);
} else {
seq->width = 1920;
seq->height = 1080;
}
if (footage->audio_tracks.size() > 0) {
MediaStream* audio_stream = footage->audio_tracks.at(0);
seq->audio_frequency = audio_stream->audio_frequency;
if (footage->audio_tracks.size() > 0) {
FootageStream* audio_stream = footage->audio_tracks.at(0);
seq->audio_frequency = audio_stream->audio_frequency;
Clip* c = new Clip(seq);
c->media = footage;
c->media_type = type;
c->media_stream = audio_stream->file_index;
c->timeline_in = 0;
c->timeline_out = footage->get_length_in_frames(seq->frame_rate);
c->track = 0;
c->clip_in = 0;
c->recalculateMaxLength();
seq->clips.append(c);
Clip* c = new Clip(seq);
c->media = media;
c->media_stream = audio_stream->file_index;
c->timeline_in = 0;
c->timeline_out = footage->get_length_in_frames(seq->frame_rate);
c->track = 0;
c->clip_in = 0;
c->recalculateMaxLength();
seq->clips.append(c);
if (footage->video_tracks.size() == 0) {
viewer_widget->waveform = true;
viewer_widget->waveform_clip = c;
viewer_widget->waveform_ms = audio_stream;
viewer_widget->update();
}
} else {
seq->audio_frequency = 48000;
}
if (footage->video_tracks.size() == 0) {
viewer_widget->waveform = true;
viewer_widget->waveform_clip = c;
viewer_widget->waveform_ms = audio_stream;
viewer_widget->update();
}
} else {
seq->audio_frequency = 48000;
}
seq->audio_layout = AV_CH_LAYOUT_STEREO;
set_sequence(false, seq);
}
break;
case MEDIA_TYPE_SEQUENCE:
set_sequence(false, static_cast<Sequence*>(media));
break;
}
seq->audio_layout = AV_CH_LAYOUT_STEREO;
}
break;
case MEDIA_TYPE_SEQUENCE:
seq = media->to_sequence();
break;
}
}
set_sequence(false, seq);
}
void Viewer::on_pushButton_clicked() {
@@ -538,7 +557,19 @@ void Viewer::recording_flasher_update() {
ui->pushButton_3->setStyleSheet("background: red;");
} else {
ui->pushButton_3->setStyleSheet(QString());
}
}
}
void Viewer::zoom_update(int i) {
if (i == 0) {
ui->glViewerPane->fit = true;
} else {
ui->glViewerPane->fit = false;
QString pc = ui->zoomComboBox->itemText(i);
pc = pc.left(pc.length() - 1);
ui->glViewerPane->zoom = pc.toDouble()*0.01;
}
ui->glViewerPane->adjust();
}
void Viewer::clean_created_seq() {
@@ -564,12 +595,10 @@ void Viewer::set_sequence(bool main, Sequence *s) {
bool null_sequence = (seq == NULL);
init_audio(seq);
ui->headers->setEnabled(!null_sequence);
ui->currentTimecode->setEnabled(!null_sequence);
ui->openGLWidget->setEnabled(!null_sequence);
ui->openGLWidget->setVisible(!null_sequence);
viewer_widget->setEnabled(!null_sequence);
viewer_widget->setVisible(!null_sequence);
ui->pushButton->setEnabled(!null_sequence);
ui->pushButton_2->setEnabled(!null_sequence);
ui->pushButton_3->setEnabled(!null_sequence);
@@ -584,7 +613,6 @@ void Viewer::set_sequence(bool main, Sequence *s) {
update_playhead_timecode(seq->playhead);
update_end_timecode();
ui->glViewerPane->aspect_ratio = (float) seq->width / (float) seq->height;
ui->glViewerPane->adjust();
setWindowTitle(panel_name + seq->name);
+9 -4
View File
@@ -6,6 +6,7 @@
class Timeline;
class ViewerWidget;
class Media;
struct Sequence;
namespace Ui {
@@ -26,7 +27,7 @@ public:
bool is_focused();
void set_main_sequence();
void set_media(int type, void* media);
void set_media(Media *m);
void compose();
void set_playpause_icon(bool play);
void update_playhead_timecode(long p);
@@ -36,6 +37,7 @@ public:
void clear_inout_point();
void set_in_point();
void set_out_point();
void set_zoom(bool in);
// playback functions
void go_to_start();
@@ -57,14 +59,14 @@ public:
bool is_recording_cued();
long recording_start;
long recording_end;
int recording_track;
int recording_track;
void reset_all_audio();
void assert_audio_device();
void reset_all_audio();
void update_parents();
ViewerWidget* viewer_widget;
Media* media;
Sequence* seq;
Ui::Viewer *ui;
@@ -81,12 +83,15 @@ private slots:
void update_playhead();
void timer_update();
void recording_flasher_update();
void zoom_update(int i);
private:
void clean_created_seq();
void set_sequence(bool main, Sequence* s);
bool main_sequence;
bool created_sequence;
long cached_end_frame;
QString panel_name;
double minimum_zoom;
bool cue_recording_internal;
QTimer recording_flasher;
+109 -15
View File
@@ -49,21 +49,120 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<widget class="ViewerWidget" name="openGLWidget">
<property name="geometry">
<rect>
<x>110</x>
<y>50</y>
<width>300</width>
<height>200</height>
</rect>
</property>
</widget>
</widget>
</item>
<item>
<widget class="TimelineHeader" name="headers" native="true"/>
</item>
<item>
<widget class="QScrollBar" name="horizontalScrollBar">
<property name="maximum">
<number>0</number>
</property>
<property name="singleStep">
<number>20</number>
</property>
<property name="pageStep">
<number>1826</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="zoomControlBar" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>260</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QComboBox" name="zoomComboBox">
<item>
<property name="text">
<string>Fit</string>
</property>
</item>
<item>
<property name="text">
<string>10%</string>
</property>
</item>
<item>
<property name="text">
<string>25%</string>
</property>
</item>
<item>
<property name="text">
<string>50%</string>
</property>
</item>
<item>
<property name="text">
<string>75%</string>
</property>
</item>
<item>
<property name="text">
<string>100%</string>
</property>
</item>
<item>
<property name="text">
<string>150%</string>
</property>
</item>
<item>
<property name="text">
<string>200%</string>
</property>
</item>
<item>
<property name="text">
<string>400%</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>259</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="playbackControls" native="true">
<property name="sizePolicy">
@@ -223,11 +322,6 @@
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ViewerWidget</class>
<extends>QOpenGLWidget</extends>
<header>ui/viewerwidget.h</header>
</customwidget>
<customwidget>
<class>ViewerContainer</class>
<extends>QWidget</extends>
+38 -28
View File
@@ -40,41 +40,51 @@ bool is_audio_device_set() {
return audio_device_set;
}
void init_audio(Sequence* s) {
void init_audio() {
stop_audio();
if (s != NULL) {
QAudioFormat audio_format;
audio_format.setSampleRate(s->audio_frequency);
audio_format.setChannelCount(av_get_channel_layout_nb_channels(s->audio_layout));
audio_format.setSampleSize(16);
audio_format.setCodec("audio/pcm");
audio_format.setByteOrder(QAudioFormat::LittleEndian);
audio_format.setSampleType(QAudioFormat::SignedInt);
QAudioFormat audio_format;
audio_format.setSampleRate(config.audio_rate);
audio_format.setChannelCount(2);
audio_format.setSampleSize(16);
audio_format.setCodec("audio/pcm");
audio_format.setByteOrder(QAudioFormat::LittleEndian);
audio_format.setSampleType(QAudioFormat::SignedInt);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
if (!info.isFormatSupported(audio_format)) {
qWarning() << "[WARNING] Audio format is not supported by backend, using nearest";
audio_format = info.nearestFormat(audio_format);
}
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
QList<QAudioDeviceInfo> devs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
dout << "[INFO] Found the following audio devices:";
for (int i=0;i<devs.size();i++) {
dout << " " << devs.at(i).deviceName();
}
if (info.isNull() && devs.size() > 0) {
dout << "[WARNING] Default audio returned NULL, attempting to use first device found...";
info = devs.at(0);
}
dout << "[INFO] Using audio device" << info.deviceName();
audio_output = new QAudioOutput(info, audio_format);
audio_output->setNotifyInterval(5);
if (!info.isFormatSupported(audio_format)) {
qWarning() << "[WARNING] Audio format is not supported by backend, using nearest";
audio_format = info.nearestFormat(audio_format);
}
// connect
audio_io_device = audio_output->start();
if (audio_io_device == NULL) {
dout << "[WARNING] Received NULL audio device. No compatible audio output was found.";
} else {
audio_device_set = true;
audio_output = new QAudioOutput(info, audio_format);
audio_output->moveToThread(QApplication::instance()->thread());
audio_output->setNotifyInterval(5);
// start sender thread
audio_thread = new AudioSenderThread();
QObject::connect(audio_output, SIGNAL(notify()), audio_thread, SLOT(notifyReceiver()));
audio_thread->start(QThread::TimeCriticalPriority);
// connect
audio_io_device = audio_output->start();
if (audio_io_device == NULL) {
dout << "[WARNING] Received NULL audio device. No compatible audio output was found.";
} else {
audio_device_set = true;
clear_audio_ibuffer();
}
// start sender thread
audio_thread = new AudioSenderThread();
QObject::connect(audio_output, SIGNAL(notify()), audio_thread, SLOT(notifyReceiver()));
audio_thread->start(QThread::TimeCriticalPriority);
clear_audio_ibuffer();
}
}
+1 -1
View File
@@ -45,7 +45,7 @@ void clear_audio_ibuffer();
bool is_audio_device_set();
void init_audio(Sequence *s);
void init_audio();
void stop_audio();
int get_buffer_offset_from_frame(double framerate, long frame);
+432 -450
View File
File diff suppressed because it is too large Load Diff
+76 -52
View File
@@ -2,7 +2,7 @@
#include "project/clip.h"
#include "project/sequence.h"
#include "io/media.h"
#include "project/footage.h"
#include "playback/audio.h"
#include "playback/cacher.h"
#include "panels/panels.h"
@@ -10,10 +10,14 @@
#include "panels/viewer.h"
#include "project/effect.h"
#include "panels/effectcontrols.h"
#include "project/media.h"
#include "io/config.h"
#include "io/avtogl.h"
#include "debug.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/pixdesc.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
@@ -32,36 +36,34 @@ extern "C" {
bool texture_failed = false;
bool rendering = false;
void open_clip(Clip* clip, bool multithreaded) {
switch (clip->media_type) {
case MEDIA_TYPE_FOOTAGE:
case MEDIA_TYPE_TONE:
clip->multithreaded = multithreaded;
if (multithreaded) {
if (clip->open_lock.tryLock()) {
// maybe keep cacher instance in memory while clip exists for performance?
clip->cacher = new Cacher(clip);
QObject::connect(clip->cacher, SIGNAL(finished()), clip->cacher, SLOT(deleteLater()));
clip->cacher->start((clip->track < 0) ? QThread::NormalPriority : QThread::TimeCriticalPriority);
}
} else {
clip->finished_opening = false;
clip->open = true;
bool clip_uses_cacher(Clip* clip) {
return (clip->media == NULL && clip->track >= 0) || (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_FOOTAGE);
}
open_clip_worker(clip);
}
break;
case MEDIA_TYPE_SEQUENCE:
case MEDIA_TYPE_SOLID:
clip->open = true;
break;
}
void open_clip(Clip* clip, bool multithreaded) {
if (clip_uses_cacher(clip)) {
clip->multithreaded = multithreaded;
if (multithreaded) {
if (clip->open_lock.tryLock()) {
// maybe keep cacher instance in memory while clip exists for performance?
clip->cacher = new Cacher(clip);
QObject::connect(clip->cacher, SIGNAL(finished()), clip->cacher, SLOT(deleteLater()));
clip->cacher->start((clip->track < 0) ? QThread::NormalPriority : QThread::TimeCriticalPriority);
}
} else {
clip->finished_opening = false;
clip->open = true;
open_clip_worker(clip);
}
} else {
clip->open = true;
}
}
void close_clip(Clip* clip) {
// destroy opengl texture in main thread
if (clip->texture != NULL) {
clip->texture->destroy();
if (clip->texture != NULL) {
delete clip->texture;
clip->texture = NULL;
}
@@ -77,26 +79,23 @@ void close_clip(Clip* clip) {
clip->fbo = NULL;
}
switch (clip->media_type) {
case MEDIA_TYPE_FOOTAGE:
case MEDIA_TYPE_TONE:
if (clip->multithreaded) {
clip->cacher->caching = false;
clip->can_cache.wakeAll();
} else {
close_clip_worker(clip);
}
break;
case MEDIA_TYPE_SEQUENCE:
closeActiveClips(static_cast<Sequence*>(clip->media), false);
case MEDIA_TYPE_SOLID:
if (clip_uses_cacher(clip)) {
if (clip->multithreaded) {
clip->cacher->caching = false;
clip->can_cache.wakeAll();
} else {
close_clip_worker(clip);
}
} else {
if (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_SEQUENCE)
closeActiveClips(clip->media->to_sequence(), false);
clip->open = false;
break;
}
}
}
void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip*>& nests) {
if (clip->media_type == MEDIA_TYPE_FOOTAGE || clip->media_type == MEDIA_TYPE_TONE) {
if (clip_uses_cacher(clip)) {
if (clip->multithreaded) {
clip->cacher->playhead = playhead;
clip->cacher->reset = reset;
@@ -111,9 +110,13 @@ void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<C
}
}
double get_timecode(Clip* c, long playhead) {
return ((double)(playhead-c->get_timeline_in_with_transition()+c->get_clip_in_with_transition())/(double)c->sequence->frame_rate);
}
void get_clip_frame(Clip* c, long playhead) {
if (c->finished_opening) {
MediaStream* ms = static_cast<Media*>(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream);
FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream);
int64_t target_pts = qMax(static_cast<int64_t>(0), playhead_to_timestamp(c, playhead));
int64_t second_pts = qRound64(av_q2d(av_inv_q(c->stream->time_base)));
@@ -194,7 +197,7 @@ void get_clip_frame(Clip* c, long playhead) {
#ifdef GCF_DEBUG
dout << "GCF ==> RESET" << target_pts << "(" << target_frame->pts << "-" << target_frame->pts+target_frame->pkt_duration << ")";
#endif
// target_frame = NULL;
if (!config.fast_seeking) target_frame = NULL;
reset = true;
c->last_invalid_ts = target_pts;
} else {
@@ -219,9 +222,30 @@ void get_clip_frame(Clip* c, long playhead) {
}
if (target_frame != NULL) {
// add gate if this is the same frame
glPixelStorei(GL_UNPACK_ROW_LENGTH, target_frame->linesize[0]/4);
c->texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, target_frame->data[0]);
int nb_components = av_pix_fmt_desc_get(static_cast<enum AVPixelFormat>(c->pix_fmt))->nb_components;
glPixelStorei(GL_UNPACK_ROW_LENGTH, target_frame->linesize[0]/nb_components);
bool copied = false;
uint8_t* data = target_frame->data[0];
int frame_size;
for (int i=0;i<c->effects.size();i++) {
Effect* e = c->effects.at(i);
if (e->enable_image) {
if (!copied) {
frame_size = target_frame->linesize[0]*target_frame->height;
data = new uint8_t[frame_size];
memcpy(data, target_frame->data[0], frame_size);
copied = true;
}
e->process_image(get_timecode(c, playhead), data, frame_size);
}
}
c->texture->setData(0, get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8, data);
if (copied) delete [] data;
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
@@ -318,11 +342,11 @@ void closeActiveClips(Sequence *s, bool wait) {
if (s != NULL) {
for (int i=0;i<s->clips.size();i++) {
Clip* c = s->clips.at(i);
if (c != NULL) {
if (c->media_type == MEDIA_TYPE_SEQUENCE) {
closeActiveClips(static_cast<Sequence*>(c->media), wait);
close_clip(c);
} else if (c->media_type == MEDIA_TYPE_FOOTAGE && c->open) {
if (c != NULL) {
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
closeActiveClips(c->media->to_sequence(), wait);
if (c->open) close_clip(c);
} else if (clip_uses_cacher(c) && c->open) {
close_clip(c);
if (c->multithreaded && wait) c->cacher->wait();
}
+2
View File
@@ -12,6 +12,7 @@ struct AVFrame;
extern bool texture_failed;
extern bool rendering;
bool clip_uses_cacher(Clip* clip);
void open_clip(Clip* clip, bool multithreaded);
void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip *> &nests);
void close_clip(Clip* clip);
@@ -20,6 +21,7 @@ void cache_video_worker(Clip* c, long playhead);
void handle_media(Sequence* sequence, long playhead, bool multithreaded);
void reset_cache(Clip* c, long target_frame);
void get_clip_frame(Clip* c, long playhead);
double get_timecode(Clip* c, long playhead);
long playhead_to_clip_frame(Clip* c, long playhead);
double playhead_to_clip_seconds(Clip* c, long playhead);
+51 -57
View File
@@ -2,13 +2,14 @@
#include "project/effect.h"
#include "project/transition.h"
#include "io/media.h"
#include "project/footage.h"
#include "io/config.h"
#include "playback/playback.h"
#include "playback/cacher.h"
#include "panels/project.h"
#include "project/sequence.h"
#include "panels/timeline.h"
#include "project/media.h"
#include "undo.h"
extern "C" {
@@ -35,7 +36,7 @@ Clip::Clip(Sequence* s) :
use_existing_frame(false),
filter_graph(NULL),
fbo(NULL),
texture(NULL)
opts(NULL)
{
pkt = av_packet_alloc();
reset();
@@ -54,7 +55,6 @@ Clip* Clip::copy(Sequence* s) {
copy->color_g = color_g;
copy->color_b = color_b;
copy->media = media;
copy->media_type = media_type;
copy->media_stream = media_stream;
copy->autoscale = autoscale;
copy->speed = speed;
@@ -88,32 +88,27 @@ void Clip::reset() {
codec = NULL;
codecCtx = NULL;
texture = NULL;
last_invalid_ts = -1;
}
void Clip::reset_audio() {
switch (media_type) {
case MEDIA_TYPE_FOOTAGE:
case MEDIA_TYPE_TONE:
if (media == NULL || media->get_type() == MEDIA_TYPE_FOOTAGE) {
audio_reset = true;
frame_sample_index = -1;
audio_buffer_write = 0;
break;
case MEDIA_TYPE_SEQUENCE:
{
Sequence* nested_sequence = static_cast<Sequence*>(media);
frame_sample_index = -1;
audio_buffer_write = 0;
} else if (media->get_type() == MEDIA_TYPE_SEQUENCE) {
Sequence* nested_sequence = media->to_sequence();
for (int i=0;i<nested_sequence->clips.size();i++) {
Clip* c = nested_sequence->clips.at(i);
if (c != NULL) c->reset_audio();
}
}
break;
}
}
void Clip::refresh() {
// validates media if it was replaced
if (replaced && media_type == MEDIA_TYPE_FOOTAGE) {
Media* m = static_cast<Media*>(media);
if (replaced && media != NULL && media->get_type() == MEDIA_TYPE_FOOTAGE) {
Footage* m = media->to_footage();
media_stream = (track < 0) ? m->video_tracks.at(0)->file_index : m->audio_tracks.at(0)->file_index;
}
replaced = false;
@@ -164,7 +159,7 @@ Clip::~Clip() {
close_clip(this);
// make sure clip has closed before clip is destroyed
if (multithreaded && media_type == MEDIA_TYPE_FOOTAGE) {
if (multithreaded && media != NULL && media->get_type() == MEDIA_TYPE_FOOTAGE) {
cacher->wait();
}
}
@@ -208,36 +203,45 @@ long Clip::getLength() {
return timeline_out - timeline_in;
}
double Clip::getMediaFrameRate() {
Q_ASSERT(track < 0);
if (media != NULL) {
double rate = media->get_frame_rate(media_stream);
if (!qIsNaN(rate)) return rate;
}
if (sequence != NULL) return sequence->frame_rate;
return qSNaN();
}
void Clip::recalculateMaxLength() {
if (sequence != NULL) {
double fr = this->sequence->frame_rate;
fr /= speed;
switch (media_type) {
case MEDIA_TYPE_FOOTAGE:
{
Media* m = static_cast<Media*>(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<Sequence*>(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;
}
calculated_length = LONG_MAX;
if (media != NULL) {
switch (media->get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
Footage* m = media->to_footage();
FootageStream* 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 = media->to_sequence();
calculated_length = refactor_frame_number(s->getEndFrame(), s->frame_rate, fr);
}
break;
}
}
}
}
@@ -245,28 +249,18 @@ long Clip::getMaximumLength() {
return calculated_length;
}
double Clip::getMediaFrameRate() {
Q_ASSERT(track < 0);
switch (media_type) {
case MEDIA_TYPE_FOOTAGE: return static_cast<Media*>(media)->get_stream_from_file_index(track < 0, media_stream)->video_frame_rate;
case MEDIA_TYPE_SEQUENCE: return static_cast<Sequence*>(media)->frame_rate;
}
if (sequence != NULL) return sequence->frame_rate;
return qSNaN();
}
int Clip::getWidth() {
if (media == NULL && sequence != NULL) return sequence->width;
switch (media_type) {
switch (media->get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
MediaStream* ms = static_cast<Media*>(media)->get_stream_from_file_index(track < 0, media_stream);
FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream);
if (ms != NULL) return ms->video_width;
if (sequence != NULL) return sequence->width;
}
case MEDIA_TYPE_SEQUENCE:
{
Sequence* s = static_cast<Sequence*>(media);
Sequence* s = media->to_sequence();
return s->width;
}
}
@@ -275,16 +269,16 @@ int Clip::getWidth() {
int Clip::getHeight() {
if (media == NULL && sequence != NULL) return sequence->height;
switch (media_type) {
switch (media->get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
MediaStream* ms = static_cast<Media*>(media)->get_stream_from_file_index(track < 0, media_stream);
FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream);
if (ms != NULL) return ms->video_height;
if (sequence != NULL) return sequence->height;
}
case MEDIA_TYPE_SEQUENCE:
{
Sequence* s = static_cast<Sequence*>(media);
Sequence* s = media->to_sequence();
return s->height;
}
}
+9 -7
View File
@@ -13,9 +13,10 @@ class Effect;
class Transition;
class QOpenGLFramebufferObject;
class ComboAction;
class Media;
struct Sequence;
struct Media;
struct MediaStream;
struct Footage;
struct FootageStream;
struct AVFormatContext;
struct AVStream;
@@ -27,6 +28,7 @@ struct SwsContext;
struct SwrContext;
struct AVFilterGraph;
struct AVFilterContext;
struct AVDictionary;
class QOpenGLTexture;
struct Clip
@@ -41,9 +43,9 @@ struct Clip
long get_timeline_in_with_transition();
long get_timeline_out_with_transition();
long getLength();
double getMediaFrameRate();
long getMaximumLength();
void recalculateMaxLength();
double getMediaFrameRate();
int getWidth();
int getHeight();
void refactor_frame_rate(ComboAction* ca, double multiplier, bool change_timeline_points);
@@ -63,8 +65,7 @@ struct Clip
quint8 color_r;
quint8 color_g;
quint8 color_b;
void* media; // attached media
int media_type;
Media* media;
int media_stream;
double speed;
double cached_fr;
@@ -87,6 +88,7 @@ struct Clip
AVCodecContext* codecCtx;
AVPacket* pkt;
AVFrame* frame;
AVDictionary* opts;
long calculated_length;
// temporary variables
@@ -96,9 +98,9 @@ struct Clip
bool pkt_written;
bool open;
bool finished_opening;
bool replaced;
int skip_type;
bool replaced;
bool ignore_reverse;
int pix_fmt;
// caching functions
bool use_existing_frame;
+38 -23
View File
@@ -58,7 +58,7 @@ Effect* create_effect(Clip* c, const EffectMeta* em) {
case EFFECT_INTERNAL_PAN: return new PanEffect(c, em);
case EFFECT_INTERNAL_TONE: return new ToneEffect(c, em);
case EFFECT_INTERNAL_SHAKE: return new ShakeEffect(c, em);
case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em);
case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em);
}
} else {
dout << "[ERROR] Invalid effect data";
@@ -107,8 +107,15 @@ void load_internal_effects() {
effects.append(em);
em.name = "Corner Pin";
em.category = "Distort";
em.internal = EFFECT_INTERNAL_CORNERPIN;
effects.append(em);
em.name = "Mask";
em.internal = EFFECT_INTERNAL_MASK;
effects.append(em);
em.name = "Shake";
em.internal = EFFECT_INTERNAL_SHAKE;
effects.append(em);
em.name = "Text";
@@ -117,20 +124,13 @@ void load_internal_effects() {
effects.append(em);
em.name = "Timecode";
em.category = "Render";
em.internal = EFFECT_INTERNAL_TIMECODE;
effects.append(em);
em.name = "Solid";
em.category = "Render";
em.name = "Solid";
em.internal = EFFECT_INTERNAL_SOLID;
effects.append(em);
em.name = "Shake";
em.category = "Distort";
em.internal = EFFECT_INTERNAL_SHAKE;
effects.append(em);
// internal transitions
em.type = EFFECT_TYPE_TRANSITION;
em.category = "";
@@ -230,6 +230,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) :
enable_shader(false),
enable_coords(false),
enable_superimpose(false),
enable_image(false),
glslProgram(NULL),
texture(NULL),
isOpen(false),
@@ -431,9 +432,14 @@ Effect::~Effect() {
close();
}
delete container;
for (int i=0;i<rows.size();i++) {
delete rows.at(i);
}
for (int i=0;i<gizmos.size();i++) {
delete gizmos.at(i);
}
}
void Effect::copy_field_keyframes(Effect* e) {
@@ -744,11 +750,11 @@ void Effect::close() {
if (!isOpen) {
dout << "[WARNING] Tried to close an effect that was already closed";
}
delete_texture();
if (glslProgram != NULL) {
delete glslProgram;
glslProgram = NULL;
}
delete_texture();
isOpen = false;
}
@@ -762,9 +768,11 @@ void Effect::startEffect() {
void Effect::endEffect() {
if (bound) glslProgram->release();
bound = false;
bound = false;
}
void Effect::process_image(double, uint8_t *, int) {}
Effect* Effect::copy(Clip* c) {
Effect* copy = create_effect(c, meta);
copy->set_enabled(is_enabled());
@@ -783,17 +791,17 @@ void Effect::process_shader(double timecode, GLTextureCoords&) {
if (!field->id.isEmpty()) {
switch (field->type) {
case EFFECT_FIELD_DOUBLE:
glslProgram->setUniformValue(field->id.toLatin1().constData(), (GLfloat) field->get_double_value(timecode));
glslProgram->setUniformValue(field->id.toUtf8().constData(), (GLfloat) field->get_double_value(timecode));
break;
case EFFECT_FIELD_COLOR:
glslProgram->setUniformValue(field->id.toLatin1().constData(), field->get_color_value(timecode).redF(), field->get_color_value(timecode).greenF(), field->get_color_value(timecode).blueF());
glslProgram->setUniformValue(field->id.toUtf8().constData(), field->get_color_value(timecode).redF(), field->get_color_value(timecode).greenF(), field->get_color_value(timecode).blueF());
break;
case EFFECT_FIELD_STRING: break; // can you even send a string to a uniform value?
case EFFECT_FIELD_BOOL:
glslProgram->setUniformValue(field->id.toLatin1().constData(), field->get_bool_value(timecode));
glslProgram->setUniformValue(field->id.toUtf8().constData(), field->get_bool_value(timecode));
break;
case EFFECT_FIELD_COMBO:
glslProgram->setUniformValue(field->id.toLatin1().constData(), field->get_combo_index(timecode));
glslProgram->setUniformValue(field->id.toUtf8().constData(), field->get_combo_index(timecode));
break;
case EFFECT_FIELD_FONT: break; // can you even send a string to a uniform value?
}
@@ -860,13 +868,21 @@ void Effect::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, doub
if (gizmos.at(i) == gizmo) {
ComboAction* ca = NULL;
if (done) ca = new ComboAction();
if (gizmo->x_field != NULL) {
gizmo->x_field->set_double_value(gizmo->x_field->get_double_value(timecode) + x_movement*gizmo->x_field_multi);
gizmo->x_field->make_key_from_change(ca);
if (gizmo->x_field1 != NULL) {
gizmo->x_field1->set_double_value(gizmo->x_field1->get_double_value(timecode) + x_movement*gizmo->x_field_multi1);
gizmo->x_field1->make_key_from_change(ca);
}
if (gizmo->y_field != NULL) {
gizmo->y_field->set_double_value(gizmo->y_field->get_double_value(timecode) + y_movement*gizmo->y_field_multi);
gizmo->y_field->make_key_from_change(ca);
if (gizmo->y_field1 != NULL) {
gizmo->y_field1->set_double_value(gizmo->y_field1->get_double_value(timecode) + y_movement*gizmo->y_field_multi1);
gizmo->y_field1->make_key_from_change(ca);
}
if (gizmo->x_field2 != NULL) {
gizmo->x_field2->set_double_value(gizmo->x_field2->get_double_value(timecode) + x_movement*gizmo->x_field_multi2);
gizmo->x_field2->make_key_from_change(ca);
}
if (gizmo->y_field2 != NULL) {
gizmo->y_field2->set_double_value(gizmo->y_field2->get_double_value(timecode) + y_movement*gizmo->y_field_multi2);
gizmo->y_field2->make_key_from_change(ca);
}
if (done) undo_stack.push(ca);
break;
@@ -975,7 +991,6 @@ bool Effect::valueHasChanged(double timecode) {
void Effect::delete_texture() {
if (texture != NULL) {
texture->destroy();
delete texture;
texture = NULL;
}
+3 -1
View File
@@ -62,7 +62,7 @@ extern QMutex effects_loaded;
#define EFFECT_INTERNAL_TONE 6
#define EFFECT_INTERNAL_SHAKE 7
#define EFFECT_INTERNAL_TIMECODE 8
#define EFFECT_INTERNAL_MASK 9
#define EFFECT_INTERNAL_CORNERPIN 12
@@ -147,12 +147,14 @@ public:
bool enable_shader;
bool enable_coords;
bool enable_superimpose;
bool enable_image;
int getIterations();
void setIterations(int i);
const char* ffmpeg_filter;
virtual void process_image(double timecode, uint8_t* data, int size);
virtual void process_shader(double timecode, GLTextureCoords&);
virtual void process_coords(double timecode, GLTextureCoords& coords, int data);
virtual GLuint process_superimpose(double timecode);
+12 -6
View File
@@ -4,10 +4,14 @@
#include "effectfield.h"
EffectGizmo::EffectGizmo(int type) :
x_field(NULL),
x_field_multi(1.0),
y_field(NULL),
y_field_multi(1.0),
x_field1(NULL),
x_field_multi1(1.0),
y_field1(NULL),
y_field_multi1(1.0),
x_field2(NULL),
x_field_multi2(1.0),
y_field2(NULL),
y_field_multi2(1.0),
type(type),
cursor(-1)
{
@@ -19,8 +23,10 @@ EffectGizmo::EffectGizmo(int type) :
}
void EffectGizmo::set_previous_value() {
if (x_field != NULL) static_cast<LabelSlider*>(x_field->ui_element)->set_previous_value();
if (y_field != NULL) static_cast<LabelSlider*>(y_field->ui_element)->set_previous_value();
if (x_field1 != NULL) static_cast<LabelSlider*>(x_field1->ui_element)->set_previous_value();
if (y_field1 != NULL) static_cast<LabelSlider*>(y_field1->ui_element)->set_previous_value();
if (x_field2 != NULL) static_cast<LabelSlider*>(x_field2->ui_element)->set_previous_value();
if (y_field2 != NULL) static_cast<LabelSlider*>(y_field2->ui_element)->set_previous_value();
}
int EffectGizmo::get_point_count() {
+10 -4
View File
@@ -3,8 +3,10 @@
#define GIZMO_TYPE_DOT 0
#define GIZMO_TYPE_POLY 1
#define GIZMO_TYPE_TARGET 2
#define GIZMO_DOT_SIZE 2.5F
#define GIZMO_TARGET_SIZE 5.0F
#include <QString>
#include <QRect>
@@ -22,10 +24,14 @@ public:
QVector<QPoint> world_pos;
QVector<QPoint> screen_pos;
EffectField* x_field;
double x_field_multi;
EffectField* y_field;
double y_field_multi;
EffectField* x_field1;
double x_field_multi1;
EffectField* y_field1;
double y_field_multi1;
EffectField* x_field2;
double x_field_multi2;
EffectField* y_field2;
double y_field_multi2;
void set_previous_value();
+1 -1
View File
@@ -30,7 +30,7 @@ EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QSt
QSize button_size(20, 20);
QSize icon_size(12, 12);
QHBoxLayout* key_controls = new QHBoxLayout();
key_controls = new QHBoxLayout();
key_controls->setSpacing(0);
key_controls->setMargin(0);
key_controls->addStretch();
+2
View File
@@ -11,6 +11,7 @@ class QLabel;
class KeyframeDelete;
class QPushButton;
class ComboAction;
class QHBoxLayout;
class EffectRow : public QObject {
Q_OBJECT
@@ -45,6 +46,7 @@ private:
int ui_row;
QVector<EffectField*> fields;
QHBoxLayout* key_controls;
QPushButton* keyframe_enable;
QPushButton* left_key_nav;
QPushButton* key_addremove;
+8 -7
View File
@@ -1,4 +1,4 @@
#include "media.h"
#include "footage.h"
#include <QDebug>
#include <QtMath>
@@ -10,15 +10,15 @@ extern "C" {
#include "project/clip.h"
Media::Media() : ready(false), preview_gen(NULL), invalid(false) {
Footage::Footage() : ready(false), preview_gen(NULL), invalid(false), in(0), out(0) {
ready_lock.lock();
}
Media::~Media() {
Footage::~Footage() {
reset();
}
void Media::reset() {
void Footage::reset() {
if (preview_gen != NULL) {
preview_gen->cancel();
preview_gen->wait();
@@ -34,11 +34,12 @@ void Media::reset() {
ready = false;
}
long Media::get_length_in_frames(double frame_rate) {
return qFloor(((double) length / (double) AV_TIME_BASE) * frame_rate);
long Footage::get_length_in_frames(double frame_rate) {
if (length >= 0) return qFloor(((double) length / (double) AV_TIME_BASE) * frame_rate);
return 0;
}
MediaStream* Media::get_stream_from_file_index(bool video, int index) {
FootageStream* Footage::get_stream_from_file_index(bool video, int index) {
if (video) {
for (int i=0;i<video_tracks.size();i++) {
if (video_tracks.at(i)->file_index == index) {
+11 -17
View File
@@ -1,5 +1,5 @@
#ifndef MEDIA_H
#define MEDIA_H
#ifndef FOOTAGE_H
#define FOOTAGE_H
#include <QString>
#include <QVector>
@@ -8,12 +8,6 @@
#include <QMutex>
#include <QPixmap>
#define MEDIA_TYPE_FOOTAGE 0
#define MEDIA_TYPE_SEQUENCE 1
#define MEDIA_TYPE_FOLDER 2
#define MEDIA_TYPE_SOLID 3
#define MEDIA_TYPE_TONE 4
#define VIDEO_PROGRESSIVE 0
#define VIDEO_TOP_FIELD_FIRST 1
#define VIDEO_BOTTOM_FIELD_FIRST 2
@@ -23,7 +17,7 @@ struct Clip;
class PreviewGenerator;
class MediaThrobber;
struct MediaStream {
struct FootageStream {
int file_index;
int video_width;
int video_height;
@@ -41,15 +35,15 @@ struct MediaStream {
QVector<char> audio_preview;
};
struct Media {
Media();
~Media();
struct Footage {
Footage();
~Footage();
QString url;
QString url;
QString name;
int64_t length;
QVector<MediaStream*> video_tracks;
QVector<MediaStream*> audio_tracks;
QVector<FootageStream*> video_tracks;
QVector<FootageStream*> audio_tracks;
int save_id;
bool ready;
bool invalid;
@@ -62,8 +56,8 @@ struct Media {
long out;
long get_length_in_frames(double frame_rate);
MediaStream* get_stream_from_file_index(bool video, int index);
FootageStream* get_stream_from_file_index(bool video, int index);
void reset();
};
#endif // MEDIA_H
#endif // FOOTAGE_H
+312
View File
@@ -0,0 +1,312 @@
#include "media.h"
#include "footage.h"
#include "sequence.h"
#include "undo.h"
#include "io/config.h"
#include "panels/viewer.h"
#include "panels/project.h"
#include "projectmodel.h"
#include "debug.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
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";
}
}
QString get_channel_layout_name(int channels, uint64_t 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);
}
}
}
Media::Media(Media* iparent) :
parent(iparent),
throbber(NULL),
root(false),
type(-1)
{}
Media::~Media() {
switch (get_type()) {
case MEDIA_TYPE_FOOTAGE: delete to_footage(); break;
case MEDIA_TYPE_SEQUENCE: if (object != NULL) delete to_sequence(); break;
}
if (throbber != NULL) delete throbber;
qDeleteAll(children);
}
Footage *Media::to_footage() {
return static_cast<Footage*>(object);
}
Sequence *Media::to_sequence() {
return static_cast<Sequence*>(object);
}
void Media::set_footage(Footage *f) {
type = MEDIA_TYPE_FOOTAGE;
object = f;
}
void Media::set_sequence(Sequence *s) {
set_icon(QIcon(":/icons/sequence.png"));
type = MEDIA_TYPE_SEQUENCE;
object = s;
if (s != NULL) update_tooltip();
}
void Media::set_folder() {
if (folder_name.isEmpty()) folder_name = "New Folder";
set_icon(QIcon(":/icons/folder.png"));
type = MEDIA_TYPE_FOLDER;
object = NULL;
}
void Media::set_icon(const QIcon &ico) {
icon = ico;
}
void Media::set_parent(Media *p) {
parent = p;
}
void Media::update_tooltip(const QString& error) {
switch (type) {
case MEDIA_TYPE_FOOTAGE:
{
Footage* f = to_footage();
tooltip = "Name: " + f->name + "\nFilename: " + f->url + "\n";
if (error.isEmpty()) {
if (f->video_tracks.size() > 0) {
tooltip += "Video Dimensions: ";
for (int i=0;i<f->video_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += QString::number(f->video_tracks.at(i)->video_width) + "x" + QString::number(f->video_tracks.at(i)->video_height);
}
tooltip += "\n";
if (!f->video_tracks.at(0)->infinite_length) {
tooltip += "Frame Rate: ";
for (int i=0;i<f->video_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
if (f->video_tracks.at(i)->video_interlacing == VIDEO_PROGRESSIVE) {
tooltip += QString::number(f->video_tracks.at(i)->video_frame_rate);
} else {
tooltip += QString::number(f->video_tracks.at(i)->video_frame_rate * 2);
tooltip += " fields (" + QString::number(f->video_tracks.at(i)->video_frame_rate) + " frames)";
}
}
tooltip += "\n";
}
tooltip += "Interlacing: ";
for (int i=0;i<f->video_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += get_interlacing_name(f->video_tracks.at(i)->video_interlacing);
}
}
if (f->audio_tracks.size() > 0) {
tooltip += "\n";
tooltip += "Audio Frequency: ";
for (int i=0;i<f->audio_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += QString::number(f->audio_tracks.at(i)->audio_frequency);
}
tooltip += "\n";
tooltip += "Audio Channels: ";
for (int i=0;i<f->audio_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += get_channel_layout_name(f->audio_tracks.at(i)->audio_channels, f->audio_tracks.at(i)->audio_layout);
}
// tooltip += "\n";
}
} else {
tooltip += error;
}
}
break;
case MEDIA_TYPE_SEQUENCE:
{
Sequence* s = to_sequence();
tooltip = "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);
}
break;
}
}
void *Media::to_object() {
return object;
}
int Media::get_type() {
return type;
}
const QString &Media::get_name() {
switch (type) {
case MEDIA_TYPE_FOOTAGE: return to_footage()->name;
case MEDIA_TYPE_SEQUENCE: return to_sequence()->name;
default: return folder_name;
}
}
void Media::set_name(const QString &n) {
switch (type) {
case MEDIA_TYPE_FOOTAGE: to_footage()->name = n; break;
case MEDIA_TYPE_SEQUENCE: to_sequence()->name = n; break;
case MEDIA_TYPE_FOLDER: folder_name = n; break;
}
}
double Media::get_frame_rate(int stream) {
switch (get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
Footage* f = to_footage();
if (stream < 0) return f->video_tracks.at(0)->video_frame_rate;
return f->get_stream_from_file_index(true, stream)->video_frame_rate;
}
case MEDIA_TYPE_SEQUENCE: return to_sequence()->frame_rate;
}
return NULL;
}
int Media::get_sampling_rate(int stream) {
switch (get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
Footage* f = to_footage();
if (stream < 0) return f->audio_tracks.at(0)->audio_frequency;
return to_footage()->get_stream_from_file_index(false, stream)->audio_frequency;
}
case MEDIA_TYPE_SEQUENCE: return to_sequence()->audio_frequency;
}
return 0;
}
void Media::appendChild(Media *child) {
child->set_parent(this);
children.append(child);
}
bool Media::setData(int col, const QVariant &value) {
if (col == 0) {
QString n = value.toString();
if (!n.isEmpty() && get_name() != n) {
undo_stack.push(new MediaRename(this, value.toString()));
return true;
}
}
return false;
}
Media *Media::child(int row) {
return children.value(row);
}
int Media::childCount() const {
return children.count();
}
int Media::columnCount() const {
return 3;
}
QVariant Media::data(int column, int role) {
switch (role) {
case Qt::DecorationRole:
if (column == 0) {
return icon;
}
break;
case Qt::DisplayRole:
switch (column) {
case 0: return (root) ? "Name" : get_name();
case 1:
if (root) return "Duration";
if (get_type() == MEDIA_TYPE_SEQUENCE) {
Sequence* s = to_sequence();
return frame_to_timecode(s->getEndFrame(), config.timecode_view, s->frame_rate);
}
if (get_type() == MEDIA_TYPE_FOOTAGE) {
Footage* f = to_footage();
double r = 30;
if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0)->video_frame_rate)) r = f->video_tracks.at(0)->video_frame_rate;
long len = f->get_length_in_frames(r);
if (len > 0) return frame_to_timecode(len, config.timecode_view, r);
}
break;
case 2:
if (root) return "Rate";
if (get_type() == MEDIA_TYPE_SEQUENCE) return QString::number(get_frame_rate()) + " FPS";
if (get_type() == MEDIA_TYPE_FOOTAGE) {
Footage* f = to_footage();
double r;
if (f->video_tracks.size() > 0 && !qIsNull(r = get_frame_rate())) {
return QString::number(get_frame_rate()) + " FPS";
} else if (f->audio_tracks.size() > 0) {
return QString::number(get_sampling_rate()) + " Hz";
}
}
break;
}
break;
case Qt::ToolTipRole:
return tooltip;
}
return QVariant();
}
int Media::row() const {
if (parent) {
return parent->children.indexOf(const_cast<Media*>(this));
}
return 0;
}
Media *Media::parentItem() {
return parent;
}
void Media::removeChild(int i) {
children.removeAt(i);
}
+64
View File
@@ -0,0 +1,64 @@
#ifndef MEDIA_H
#define MEDIA_H
#include <QList>
#include <QVariant>
#define MEDIA_TYPE_FOOTAGE 0
#define MEDIA_TYPE_SEQUENCE 1
#define MEDIA_TYPE_FOLDER 2
struct Footage;
class MediaThrobber;
struct Sequence;
#include <QIcon>
class Media
{
public:
Media(Media* iparent);
~Media();
Footage *to_footage();
Sequence* to_sequence();
void set_footage(Footage* f);
void set_sequence(Sequence* s);
void set_folder();
void set_icon(const QIcon &ico);
void set_parent(Media* p);
void update_tooltip(const QString& error = 0);
void *to_object();
int get_type();
const QString& get_name();
void set_name(const QString& n);
MediaThrobber* throbber;
double get_frame_rate(int stream = -1);
int get_sampling_rate(int stream = -1);
// item functions
void appendChild(Media *child);
bool setData(int col, const QVariant &value);
Media *child(int row);
int childCount() const;
int columnCount() const;
QVariant data(int column, int role);
int row() const;
Media *parentItem();
void removeChild(int i);
bool root;
int temp_id;
int temp_id2;
private:
int type;
void* object;
// item functions
QList<Media*> children;
Media* parent;
QString folder_name;
QString tooltip;
QIcon icon;
};
#endif // MEDIA_H
+178
View File
@@ -0,0 +1,178 @@
#include "projectmodel.h"
#include "panels/panels.h"
#include "panels/viewer.h"
#include "ui/viewerwidget.h"
#include "project/media.h"
#include "debug.h"
ProjectModel::ProjectModel(QObject *parent) : QAbstractItemModel(parent), root_item(NULL) {
root_item = new Media(0);
root_item->root = true;
}
ProjectModel::~ProjectModel() {
destroy_root();
}
void ProjectModel::destroy_root() {
if (panel_sequence_viewer != NULL) panel_sequence_viewer->viewer_widget->delete_function();
if (panel_footage_viewer != NULL) panel_footage_viewer->viewer_widget->delete_function();
if (root_item != NULL) {
delete root_item;
}
}
void ProjectModel::clear() {
beginResetModel();
destroy_root();
root_item = new Media(0);
root_item->root = true;
endResetModel();
}
Media *ProjectModel::get_root() {
return root_item;
}
QVariant ProjectModel::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return QVariant();
return static_cast<Media*>(index.internalPointer())->data(index.column(), role);
}
Qt::ItemFlags ProjectModel::flags(const QModelIndex &index) const {
if (!index.isValid())
return Qt::ItemIsDropEnabled;
return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
}
QVariant ProjectModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return root_item->data(section, role);
return QVariant();
}
QModelIndex ProjectModel::index(int row, int column, const QModelIndex &parent) const {
if (!hasIndex(row, column, parent))
return QModelIndex();
Media *parentItem;
if (!parent.isValid())
parentItem = root_item;
else
parentItem = static_cast<Media*>(parent.internalPointer());
Media *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex ProjectModel::parent(const QModelIndex &index) const {
if (!index.isValid())
return QModelIndex();
Media *childItem = static_cast<Media*>(index.internalPointer());
Media *parentItem = childItem->parentItem();
if (parentItem == root_item)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
bool ProjectModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (role != Qt::EditRole)
return false;
Media *item = static_cast<Media*>(index.internalPointer());
bool result = item->setData(index.column(), value);
if (result)
emit dataChanged(index, index);
return result;
}
int ProjectModel::rowCount(const QModelIndex &parent) const {
Media *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid()) {
parentItem = root_item;
} else {
parentItem = static_cast<Media*>(parent.internalPointer());
}
return parentItem->childCount();
}
int ProjectModel::columnCount(const QModelIndex &parent) const {
if (parent.isValid())
return static_cast<Media*>(parent.internalPointer())->columnCount();
else
return root_item->columnCount();
}
Media *ProjectModel::getItem(const QModelIndex &index) const {
if (index.isValid()) {
Media *item = static_cast<Media*>(index.internalPointer());
if (item)
return item;
}
return root_item;
}
void ProjectModel::set_icon(Media* m, const QIcon &ico) {
QModelIndex index = createIndex(m->row(), 0, m);
m->set_icon(ico);
emit dataChanged(index, index);
}
void ProjectModel::appendChild(Media *parent, Media *child) {
if (parent == NULL) parent = root_item;
beginInsertRows(parent == root_item ? QModelIndex() : createIndex(parent->row(), 0, parent), parent->childCount(), parent->childCount());
parent->appendChild(child);
endInsertRows();
}
void ProjectModel::moveChild(Media *child, Media *to) {
if (to == NULL) to = root_item;
Media* from = child->parentItem();
beginMoveRows(
from == root_item ? QModelIndex() : createIndex(from->row(), 0, from),
child->row(),
child->row(),
to == root_item ? QModelIndex() : createIndex(to->row(), 0, to),
to->childCount()
);
from->removeChild(child->row());
to->appendChild(child);
endMoveRows();
}
void ProjectModel::removeChild(Media* parent, Media* m) {
if (parent == NULL) parent = root_item;
beginRemoveRows(parent == root_item ? QModelIndex() : createIndex(parent->row(), 0, parent), m->row(), m->row());
parent->removeChild(m->row());
endRemoveRows();
}
Media* ProjectModel::child(int i, Media* parent) {
if (parent == NULL) parent = root_item;
return parent->child(i);
}
int ProjectModel::childCount(Media *parent) {
if (parent == NULL) parent = root_item;
return parent->childCount();
}
+41
View File
@@ -0,0 +1,41 @@
#ifndef PROJECTMODEL_H
#define PROJECTMODEL_H
#include <QAbstractItemModel>
class Media;
class ProjectModel : public QAbstractItemModel
{
Q_OBJECT
public:
ProjectModel(QObject* parent = 0);
~ProjectModel() override;
void destroy_root();
void clear();
Media* get_root();
QVariant data(const QModelIndex &index, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
Media *getItem(const QModelIndex &index) const;
void appendChild(Media* parent, Media* child);
void moveChild(Media *child, Media *to);
void removeChild(Media *parent, Media* m);
Media *child(int i, Media* parent = NULL);
int childCount(Media* parent = NULL);
void set_icon(Media* m, const QIcon &ico);
private:
Media* root_item;
};
#endif // PROJECTMODEL_H
+67 -134
View File
@@ -15,13 +15,14 @@
#include "ui/sourcetable.h"
#include "project/effect.h"
#include "project/transition.h"
#include "io/media.h"
#include "project/footage.h"
#include "playback/cacher.h"
#include "ui/labelslider.h"
#include "ui/viewerwidget.h"
#include "project/marker.h"
#include "mainwindow.h"
#include "io/clipboard.h"
#include "project/media.h"
#include "debug.h"
QUndoStack undo_stack;
@@ -169,7 +170,7 @@ void SetTimelineInOutCommand::undo() {
// footage viewer functions
if (seq->wrapper_sequence) {
Media* m = static_cast<Media*>(seq->clips.at(0)->media);
Footage* m = seq->clips.at(0)->media->to_footage();
m->using_inout = old_enabled;
m->in = old_in;
m->out = old_out;
@@ -189,7 +190,7 @@ void SetTimelineInOutCommand::redo() {
// footage viewer functions
if (seq->wrapper_sequence) {
Media* m = static_cast<Media*>(seq->clips.at(0)->media);
Footage* m = seq->clips.at(0)->media->to_footage();
m->using_inout = new_enabled;
m->in = new_in;
m->out = new_out;
@@ -198,10 +199,11 @@ void SetTimelineInOutCommand::redo() {
mainWindow->setWindowModified(true);
}
AddEffectCommand::AddEffectCommand(Clip* c, Effect* e, const EffectMeta *m) :
AddEffectCommand::AddEffectCommand(Clip* c, Effect* e, const EffectMeta *m, int insert_pos) :
clip(c),
meta(m),
ref(e),
pos(insert_pos),
done(false),
old_project_changed(mainWindow->isWindowModified())
{}
@@ -212,7 +214,11 @@ AddEffectCommand::~AddEffectCommand() {
void AddEffectCommand::undo() {
clip->effects.last()->close();
clip->effects.removeLast();
if (pos < 0) {
clip->effects.removeLast();
} else {
clip->effects.removeAt(pos);
}
done = false;
mainWindow->setWindowModified(old_project_changed);
}
@@ -221,7 +227,11 @@ void AddEffectCommand::redo() {
if (ref == NULL) {
ref = create_effect(clip, meta);
}
clip->effects.append(ref);
if (pos < 0) {
clip->effects.append(ref);
} else {
clip->effects.insert(pos, ref);
}
done = true;
mainWindow->setWindowModified(true);
}
@@ -340,38 +350,34 @@ void DeleteTransitionCommand::redo() {
mainWindow->setWindowModified(true);
}
NewSequenceCommand::NewSequenceCommand(QTreeWidgetItem *s, QTreeWidgetItem* iparent) :
NewSequenceCommand::NewSequenceCommand(Media *s, Media* iparent) :
seq(s),
parent(iparent),
done(false),
old_project_changed(mainWindow->isWindowModified())
{}
{
if (parent == NULL) parent = project_model.get_root();
}
NewSequenceCommand::~NewSequenceCommand() {
if (!done) delete seq;
}
void NewSequenceCommand::undo() {
if (parent == NULL) {
panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(seq));
} else {
parent->removeChild(seq);
}
project_model.removeChild(parent, seq);
done = false;
mainWindow->setWindowModified(old_project_changed);
}
void NewSequenceCommand::redo() {
if (parent == NULL) {
panel_project->source_table->addTopLevelItem(seq);
} else {
parent->addChild(seq);
}
project_model.appendChild(parent, seq);
done = true;
mainWindow->setWindowModified(true);
}
AddMediaCommand::AddMediaCommand(QTreeWidgetItem* iitem, QTreeWidgetItem* iparent) :
AddMediaCommand::AddMediaCommand(Media* iitem, Media *iparent) :
item(iitem),
parent(iparent),
done(false),
@@ -380,85 +386,44 @@ AddMediaCommand::AddMediaCommand(QTreeWidgetItem* iitem, QTreeWidgetItem* iparen
AddMediaCommand::~AddMediaCommand() {
if (!done) {
panel_project->delete_media(item);
if (item->data(0, Qt::UserRole + 5) != 0) delete reinterpret_cast<MediaThrobber*>(item->data(0, Qt::UserRole + 5).value<quintptr>());
delete item;
}
}
void AddMediaCommand::undo() {
if (parent == NULL) {
panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(item));
} else {
parent->removeChild(item);
}
project_model.removeChild(parent, item);
done = false;
mainWindow->setWindowModified(old_project_changed);
}
void AddMediaCommand::redo() {
if (parent == NULL) {
panel_project->source_table->addTopLevelItem(item);
} else {
parent->addChild(item);
}
/* Here we force the source_table to sort itself.
*
* For some reason, sometimes when you add items to the QTreeWidget,
* (usually upon first import) they appear at the bottom, regardless
* of where they should be placed alphabetically. Then when this
* function is "undone", and it tries to remove this item from the
* QTreeWidget, it immediately sorts and then removes THE WRONG ONE.
* If this happens to be a sequence, the sequence data doesn't save
* and is then lost forever (outside of autorecoveries).
*
* The following 2 lines seem to force the source_table to re-sort
* correctly and therefore works around this problem. But holy shit.
*
* I guess I'm "supposed" to use a Modelviewviewmodel instead,
* which I'll probably have to switch to soon anyway. So perhaps
* this will be a non-issue soon.
*/
panel_project->source_table->setSortingEnabled(false);
panel_project->source_table->setSortingEnabled(true);
project_model.appendChild(parent, item);
done = true;
mainWindow->setWindowModified(true);
}
DeleteMediaCommand::DeleteMediaCommand(QTreeWidgetItem* i) :
DeleteMediaCommand::DeleteMediaCommand(Media* i) :
item(i),
parent(i->parentItem()),
old_project_changed(mainWindow->isWindowModified())
{}
DeleteMediaCommand::~DeleteMediaCommand() {
if (done) {
panel_project->delete_media(item);
if (item->data(0, Qt::UserRole + 5) != 0) delete reinterpret_cast<MediaThrobber*>(item->data(0, Qt::UserRole + 5).value<quintptr>());
delete item;
if (done) {
delete item;
}
}
void DeleteMediaCommand::undo() {
if (parent == NULL) {
panel_project->source_table->addTopLevelItem(item);
} else {
parent->addChild(item);
}
project_model.appendChild(parent, item);
mainWindow->setWindowModified(old_project_changed);
done = false;
}
void DeleteMediaCommand::redo() {
parent = item->parent();
if (parent == NULL) {
panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(item));
} else {
parent->removeChild(item);
}
project_model.removeChild(parent, item);
mainWindow->setWindowModified(true);
done = true;
@@ -601,25 +566,24 @@ void CheckboxCommand::redo() {
mainWindow->setWindowModified(true);
}
ReplaceMediaCommand::ReplaceMediaCommand(QTreeWidgetItem* i, QString s) :
ReplaceMediaCommand::ReplaceMediaCommand(Media* i, QString s) :
item(i),
new_filename(s),
old_project_changed(mainWindow->isWindowModified())
{
media = get_footage_from_tree(item);
old_filename = media->url;
old_filename = item->to_footage()->url;
}
void ReplaceMediaCommand::replace(QString& filename) {
// close any clips currently using this media
QVector<Sequence*> all_sequences = panel_project->list_all_project_sequences();
QVector<Media*> all_sequences = panel_project->list_all_project_sequences();
for (int i=0;i<all_sequences.size();i++) {
Sequence* s = all_sequences.at(i);
Sequence* s = all_sequences.at(i)->to_sequence();
for (int j=0;j<s->clips.size();j++) {
Clip* c = s->clips.at(j);
if (c != NULL && c->media == media && c->open) {
if (c != NULL && c->media == item && c->open) {
close_clip(c);
if (c->media_type == MEDIA_TYPE_FOOTAGE) c->cacher->wait();
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) c->cacher->wait();
c->replaced = true;
}
}
@@ -628,7 +592,7 @@ void ReplaceMediaCommand::replace(QString& filename) {
// replace media
QStringList files;
files.append(filename);
panel_project->process_file_list(false, files, NULL, item);
panel_project->process_file_list(files, false, item, NULL);
}
void ReplaceMediaCommand::undo() {
@@ -643,11 +607,9 @@ void ReplaceMediaCommand::redo() {
mainWindow->setWindowModified(true);
}
ReplaceClipMediaCommand::ReplaceClipMediaCommand(void *a, void *b, int c, int d, bool e) :
ReplaceClipMediaCommand::ReplaceClipMediaCommand(Media *a, Media *b, bool e) :
old_media(a),
new_media(b),
old_type(c),
new_type(d),
new_media(b),
preserve_clip_ins(e),
old_project_changed(mainWindow->isWindowModified())
{}
@@ -661,7 +623,7 @@ void ReplaceClipMediaCommand::replace(bool undo) {
Clip* c = clips.at(i);
if (c->open) {
close_clip(c);
if (c->media_type == MEDIA_TYPE_FOOTAGE) c->cacher->wait();
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) c->cacher->wait();
}
if (undo) {
@@ -669,16 +631,14 @@ void ReplaceClipMediaCommand::replace(bool undo) {
c->clip_in = old_clip_ins.at(i);
}
c->media = old_media;
c->media_type = old_type;
c->media = old_media;
} else {
if (!preserve_clip_ins) {
old_clip_ins.append(c->clip_in);
c->clip_in = 0;
}
c->media = new_media;
c->media_type = new_type;
c->media = new_media;
}
c->replaced = true;
@@ -695,6 +655,7 @@ void ReplaceClipMediaCommand::undo() {
void ReplaceClipMediaCommand::redo() {
replace(false);
update_ui(true);
mainWindow->setWindowModified(true);
}
@@ -737,61 +698,37 @@ MediaMove::MediaMove(SourceTable *s) : table(s), old_project_changed(mainWindow-
void MediaMove::undo() {
for (int i=0;i<items.size();i++) {
if (to == NULL) {
table->takeTopLevelItem(table->indexOfTopLevelItem(items.at(i)));
} else {
to->removeChild(items.at(i));
}
}
for (int i=0;i<items.size();i++) {
if (froms.at(i) == NULL) {
table->addTopLevelItem(items.at(i));
} else {
froms.at(i)->addChild(items.at(i));
}
}
project_model.moveChild(items.at(i), froms.at(i));
}
mainWindow->setWindowModified(old_project_changed);
}
void MediaMove::redo() {
if (to == NULL) to = project_model.get_root();
froms.resize(items.size());
for (int i=0;i<items.size();i++) {
QTreeWidgetItem* parent = items.at(i)->parent();
Media* parent = items.at(i)->parentItem();
froms[i] = parent;
if (parent == NULL) {
table->takeTopLevelItem(table->indexOfTopLevelItem(items.at(i)));
} else {
parent->removeChild(items.at(i));
}
if (to == NULL) {
table->addTopLevelItem(items.at(i));
} else {
to->addChild(items.at(i));
}
}
for (int i=0;i<items.size();i++) {
if (to == NULL) {
table->addTopLevelItem(items.at(i));
} else {
to->addChild(items.at(i));
}
}
project_model.moveChild(items.at(i), to);
}
mainWindow->setWindowModified(true);
}
MediaRename::MediaRename() : done(true), old_project_changed(mainWindow->isWindowModified()) {}
MediaRename::MediaRename(Media* iitem, QString ito) :
item(iitem),
from(iitem->get_name()),
to(ito),
old_project_changed(mainWindow->isWindowModified())
{}
void MediaRename::undo() {
item->setText(0, from);
done = false;
mainWindow->setWindowModified(old_project_changed);
item->set_name(from);
mainWindow->setWindowModified(old_project_changed);
}
void MediaRename::redo() {
if (!done) {
item->setText(0, to);
}
mainWindow->setWindowModified(true);
item->set_name(to);
mainWindow->setWindowModified(true);
}
KeyframeMove::KeyframeMove() : old_project_changed(mainWindow->isWindowModified()) {}
@@ -1138,7 +1075,7 @@ void SetEnableCommand::redo() {
mainWindow->setWindowModified(true);
}
EditSequenceCommand::EditSequenceCommand(QTreeWidgetItem* i, Sequence *s) :
EditSequenceCommand::EditSequenceCommand(Media* i, Sequence *s) :
item(i),
seq(s),
old_project_changed(mainWindow->isWindowModified()),
@@ -1175,11 +1112,8 @@ void EditSequenceCommand::redo() {
}
void EditSequenceCommand::update() {
// update name
item->setText(0, seq->name);
// update tooltip
set_sequence_of_tree(item, seq);
item->set_sequence(seq);
for (int i=0;i<seq->clips.size();i++) {
// TODO shift in/out/clipin points to match new frame rate
@@ -1237,9 +1171,8 @@ void CloseAllClipsCommand::redo() {
closeActiveClips(sequence, true);
}
UpdateFootageTooltip::UpdateFootageTooltip(QTreeWidgetItem *i, Media *m) :
item(i),
media(m)
UpdateFootageTooltip::UpdateFootageTooltip(Media *i) :
item(i)
{}
void UpdateFootageTooltip::undo() {
@@ -1247,7 +1180,7 @@ void UpdateFootageTooltip::undo() {
}
void UpdateFootageTooltip::redo() {
update_footage_tooltip(item, media);
item->update_tooltip();
}
MoveEffectCommand::MoveEffectCommand() :
+34 -37
View File
@@ -1,7 +1,7 @@
#ifndef UNDO_H
#define UNDO_H
class QTreeWidgetItem;
class Media;
class QCheckBox;
class LabelSlider;
class Effect;
@@ -12,7 +12,7 @@ class Transition;
class EffectGizmo;
struct Clip;
struct Sequence;
struct Media;
struct Footage;
struct EffectMeta;
#include "project/marker.h"
@@ -22,6 +22,7 @@ struct EffectMeta;
#include <QUndoCommand>
#include <QVector>
#include <QVariant>
#include <QModelIndex>
extern QUndoStack undo_stack;
@@ -88,7 +89,7 @@ private:
class AddEffectCommand : public QUndoCommand {
public:
AddEffectCommand(Clip* c, Effect *e, const EffectMeta* m);
AddEffectCommand(Clip* c, Effect *e, const EffectMeta* m, int insert_pos = -1);
~AddEffectCommand();
void undo();
void redo();
@@ -96,6 +97,7 @@ private:
Clip* clip;
const EffectMeta* meta;
Effect* ref;
int pos;
bool done;
bool old_project_changed;
};
@@ -166,39 +168,39 @@ private:
class NewSequenceCommand : public QUndoCommand {
public:
NewSequenceCommand(QTreeWidgetItem *s, QTreeWidgetItem* iparent);
NewSequenceCommand(Media *s, Media* iparent);
~NewSequenceCommand();
void undo();
void redo();
private:
QTreeWidgetItem* seq;
QTreeWidgetItem* parent;
Media* seq;
Media* parent;
bool done;
bool old_project_changed;
};
class AddMediaCommand : public QUndoCommand {
public:
AddMediaCommand(QTreeWidgetItem* iitem, QTreeWidgetItem* iparent);
AddMediaCommand(Media* iitem, Media* iparent);
~AddMediaCommand();
void undo();
void redo();
private:
QTreeWidgetItem* item;
QTreeWidgetItem* parent;
Media* item;
Media* parent;
bool done;
bool old_project_changed;
};
class DeleteMediaCommand : public QUndoCommand {
public:
DeleteMediaCommand(QTreeWidgetItem* i);
DeleteMediaCommand(Media *i);
~DeleteMediaCommand();
void undo();
void redo();
private:
QTreeWidgetItem* item;
QTreeWidgetItem* parent;
Media* item;
Media* parent;
bool old_project_changed;
bool done;
};
@@ -258,29 +260,26 @@ private:
class ReplaceMediaCommand : public QUndoCommand {
public:
ReplaceMediaCommand(QTreeWidgetItem*, QString);
ReplaceMediaCommand(Media*, QString);
void undo();
void redo();
private:
QTreeWidgetItem *item;
Media *item;
QString old_filename;
QString new_filename;
bool old_project_changed;
Media* media;
bool old_project_changed;
void replace(QString& filename);
};
class ReplaceClipMediaCommand : public QUndoCommand {
public:
ReplaceClipMediaCommand(void*, void*, int, int, bool);
ReplaceClipMediaCommand(Media *, Media *, bool);
void undo();
void redo();
QVector<Clip*> clips;
private:
void* old_media;
void* new_media;
int old_type;
int new_type;
Media* old_media;
Media* new_media;
bool preserve_clip_ins;
bool old_project_changed;
QVector<int> old_clip_ins;
@@ -304,27 +303,26 @@ private:
class MediaMove : public QUndoCommand {
public:
MediaMove(SourceTable* s);
QVector<QTreeWidgetItem*> items;
QTreeWidgetItem* to;
QVector<Media*> items;
Media* to;
void undo();
void redo();
private:
QVector<QTreeWidgetItem*> froms;
QVector<Media*> froms;
SourceTable* table;
bool old_project_changed;
};
class MediaRename : public QUndoCommand {
public:
MediaRename();
QTreeWidgetItem* item;
QString from;
QString to;
void undo();
void redo();
MediaRename(Media* iitem, QString to);
void undo();
void redo();
private:
bool done;
bool old_project_changed;
bool old_project_changed;
Media* item;
QString from;
QString to;
};
class KeyframeMove : public QUndoCommand {
@@ -486,7 +484,7 @@ private:
class EditSequenceCommand : public QUndoCommand {
public:
EditSequenceCommand(QTreeWidgetItem *i, Sequence* s);
EditSequenceCommand(Media *i, Sequence* s);
void undo();
void redo();
void update();
@@ -498,7 +496,7 @@ public:
int audio_frequency;
int audio_layout;
private:
QTreeWidgetItem* item;
Media* item;
Sequence* seq;
bool old_project_changed;
@@ -542,12 +540,11 @@ public:
class UpdateFootageTooltip : public QUndoCommand {
public:
UpdateFootageTooltip(QTreeWidgetItem* i, Media* m);
UpdateFootageTooltip(Media* i);
void undo();
void redo();
private:
QTreeWidgetItem* item;
Media* media;
Media* item;
};
class MoveEffectCommand : public QUndoCommand {
+4 -2
View File
@@ -12,6 +12,8 @@
#include <QWidget>
#include <QPainter>
#include "debug.h"
CollapsibleWidget::CollapsibleWidget(QWidget* parent) : QWidget(parent) {
selected = false;
@@ -22,7 +24,7 @@ CollapsibleWidget::CollapsibleWidget(QWidget* parent) : QWidget(parent) {
title_bar = new CollapsibleWidgetHeader();
title_bar->setFocusPolicy(Qt::ClickFocus);
title_bar->setAutoFillBackground(true);
QHBoxLayout* title_bar_layout = new QHBoxLayout();
title_bar_layout = new QHBoxLayout();
title_bar_layout->setMargin(5);
title_bar->setLayout(title_bar_layout);
enabled_check = new CheckboxEx();
@@ -48,7 +50,7 @@ void CollapsibleWidget::header_click(bool s, bool deselect) {
selected = s;
title_bar->selected = s;
if (s) {
QPalette p = palette();
QPalette p = title_bar->palette();
p.setColor(QPalette::Background, QColor(255, 255, 255, 64));
title_bar->setPalette(p);
} else {
+1
View File
@@ -41,6 +41,7 @@ private:
QVBoxLayout* layout;
QPushButton* collapse_button;
QFrame* line;
QHBoxLayout* title_bar_layout;
signals:
void deselect_others(QWidget*);
+3 -3
View File
@@ -19,10 +19,10 @@ LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) {
setStyleSheet("QLabel{color:#ffc000;text-decoration:underline;}QLabel:disabled{color:#808080;}");
setCursor(Qt::SizeHorCursor);
internal_value = -1;
set = false;
display_type = LABELSLIDER_NORMAL;
set_default_value(0);
set_value(0, false);
set = false;
display_type = LABELSLIDER_NORMAL;
}
void LabelSlider::set_frame_rate(double d) {
+84 -74
View File
@@ -1,7 +1,7 @@
#include "sourcetable.h"
#include "panels/project.h"
#include "io/media.h"
#include "project/footage.h"
#include "panels/timeline.h"
#include "panels/viewer.h"
#include "panels/panels.h"
@@ -10,6 +10,8 @@
#include "project/sequence.h"
#include "mainwindow.h"
#include "io/config.h"
#include "project/media.h"
#include "debug.h"
#include <QDragEnterEvent>
#include <QMimeData>
@@ -22,34 +24,37 @@
#include <QDir>
#include <QProcess>
SourceTable::SourceTable(QWidget* parent) : QTreeWidget(parent) {
SourceTable::SourceTable(QWidget* parent) : QTreeView(parent) {
editing_item = NULL;
setSortingEnabled(true);
sortByColumn(0, Qt::AscendingOrder);
rename_timer.setInterval(1000);
setContextMenuPolicy(Qt::CustomContextMenu);
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()));
connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(item_click(const QModelIndex&)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu()));
}
void SourceTable::show_context_menu() {
QMenu menu(this);
QAction* import_action = menu.addAction("Import...");
connect(import_action, SIGNAL(triggered(bool)), panel_project, SLOT(import_dialog()));
connect(import_action, SIGNAL(triggered(bool)), project_parent, SLOT(import_dialog()));
QAction* new_folder_action = menu.addAction("New Folder...");
connect(new_folder_action, SIGNAL(triggered(bool)), mainWindow, SLOT(on_actionFolder_triggered()));
if (selectedItems().size() > 0) {
if (selectedItems().size() == 1) {
QModelIndexList selected_items = selectionModel()->selectedRows();
if (selected_items.size() > 0) {
Media* m = project_parent->item_to_media(selected_items.at(0));
if (selected_items.size() == 1) {
// replace footage
int type = get_type_from_tree(selectedItems().at(0));
int type = m->get_type();
if (type == MEDIA_TYPE_FOOTAGE) {
QAction* replace_action = menu.addAction("Replace/Relink Media");
connect(replace_action, SIGNAL(triggered(bool)), panel_project, SLOT(replace_selected_file()));
connect(replace_action, SIGNAL(triggered(bool)), project_parent, SLOT(replace_selected_file()));
#if defined(Q_OS_WIN)
QAction* reveal_in_explorer = menu.addAction("Reveal in Explorer");
@@ -62,18 +67,18 @@ void SourceTable::show_context_menu() {
}
if (type != MEDIA_TYPE_FOLDER) {
QAction* replace_clip_media = menu.addAction("Replace Clips Using This Media");
connect(replace_clip_media, SIGNAL(triggered(bool)), panel_project, SLOT(replace_clip_media()));
connect(replace_clip_media, SIGNAL(triggered(bool)), project_parent, SLOT(replace_clip_media()));
}
}
// duplicate item
bool all_sequences = true;
bool all_footage = true;
for (int i=0;i<selectedItems().size();i++) {
if (get_type_from_tree(selectedItems().at(i)) != MEDIA_TYPE_SEQUENCE) {
for (int i=0;i<selected_items.size();i++) {
if (m->get_type() != MEDIA_TYPE_SEQUENCE) {
all_sequences = false;
}
if (get_type_from_tree(selectedItems().at(i)) != MEDIA_TYPE_FOOTAGE) {
if (m->get_type() != MEDIA_TYPE_FOOTAGE) {
all_footage = false;
}
}
@@ -86,22 +91,22 @@ void SourceTable::show_context_menu() {
if (all_sequences) {
// ONLY sequences are selected
QAction* duplicate_action = menu.addAction("Duplicate");
connect(duplicate_action, SIGNAL(triggered(bool)), panel_project, SLOT(duplicate_selected()));
connect(duplicate_action, SIGNAL(triggered(bool)), project_parent, SLOT(duplicate_selected()));
}
// ONLY footage is selected
if (all_footage) {
QAction* delete_footage_from_sequences = menu.addAction("Delete All Clips Using This Media");
connect(delete_footage_from_sequences, SIGNAL(triggered(bool)), panel_project, SLOT(delete_clips_using_selected_media()));
connect(delete_footage_from_sequences, SIGNAL(triggered(bool)), project_parent, SLOT(delete_clips_using_selected_media()));
}
// delete media
QAction* delete_action = menu.addAction("Delete");
connect(delete_action, SIGNAL(triggered(bool)), panel_project, SLOT(delete_selected_media()));
connect(delete_action, SIGNAL(triggered(bool)), project_parent, SLOT(delete_selected_media()));
if (selectedItems().size() == 1) {
if (selected_items.size() == 1) {
QAction* properties_action = menu.addAction("Properties...");
connect(properties_action, SIGNAL(triggered(bool)), panel_project, SLOT(open_properties()));
connect(properties_action, SIGNAL(triggered(bool)), project_parent, SLOT(open_properties()));
}
}
@@ -109,29 +114,30 @@ void SourceTable::show_context_menu() {
}
void SourceTable::create_seq_from_selected() {
if (!selectedItems().isEmpty()) {
QVector<void*> media_list;
QVector<int> type_list;
for (int i=0;i<selectedItems().size();i++) {
QTreeWidgetItem* item = selectedItems().at(i);
media_list.append(get_media_from_tree(item));
type_list.append(get_type_from_tree(item));
QModelIndexList selected_items = selectionModel()->selectedRows();
if (!selected_items.isEmpty()) {
QVector<Media*> media_list;
for (int i=0;i<selected_items.size();i++) {
media_list.append(project_parent->item_to_media(selected_items.at(i)));
}
ComboAction* ca = new ComboAction();
Sequence* s = create_sequence_from_media(media_list, type_list);
Sequence* s = create_sequence_from_media(media_list);
// add clips to it
panel_timeline->create_ghosts_from_media(s, 0, media_list, type_list);
panel_timeline->create_ghosts_from_media(s, 0, media_list);
panel_timeline->add_clips_from_ghosts(ca, s);
panel_project->new_sequence(ca, s, true, NULL);
project_parent->new_sequence(ca, s, true, NULL);
undo_stack.push(ca);
}
}
void SourceTable::reveal_in_browser() {
Media* m = get_footage_from_tree(selectedItems().at(0));
QModelIndexList selected_items = selectionModel()->selectedRows();
Media* media = project_parent->item_to_media(selected_items.at(0));
Footage* m = media->to_footage();
#if defined(Q_OS_WIN)
QStringList args;
@@ -153,12 +159,9 @@ void SourceTable::reveal_in_browser() {
#endif
}
void SourceTable::item_renamed(QTreeWidgetItem* item) {
void SourceTable::item_renamed(Media* item) {
if (editing_item == item) {
MediaRename* mr = new MediaRename();
mr->from = editing_item_name;
mr->item = editing_item;
mr->to = editing_item->text(0);
MediaRename* mr = new MediaRename(item, "idk");
undo_stack.push(mr);
editing_item = NULL;
}
@@ -171,37 +174,41 @@ void SourceTable::stop_rename_timer() {
void SourceTable::rename_interval() {
stop_rename_timer();
if (hasFocus() && editing_item != NULL) {
editing_item_name = editing_item->text(0);
editItem(editing_item, 0);
edit(editing_index);
//editItem(editing_item, 0);
}
}
void SourceTable::item_click(QTreeWidgetItem* item, int column) {
if (column == 0 && selectedItems().size() == 1) {
if (editing_item == item) {
void SourceTable::item_click(const QModelIndex& index) {
if (selectionModel()->selectedRows().size() == 1 && index.column() == 0) {
Media* m = project_parent->item_to_media(index);
if (editing_item == m) {
rename_timer.start();
} else {
editing_item = m;
editing_index = index;
}
editing_item = item;
}
}
void SourceTable::mousePressEvent(QMouseEvent* event) {
stop_rename_timer();
QTreeWidget::mousePressEvent(event);
QTreeView::mousePressEvent(event);
}
void SourceTable::mouseDoubleClickEvent(QMouseEvent* ) {
void SourceTable::mouseDoubleClickEvent(QMouseEvent* e) {
stop_rename_timer();
if (selectedItems().count() == 0) {
panel_project->import_dialog();
} else if (selectedItems().count() == 1) {
QTreeWidgetItem* item = selectedItems().at(0);
switch (get_type_from_tree(item)) {
QModelIndexList selected_items = selectionModel()->selectedRows();
if (selected_items.size() == 0) {
project_parent->import_dialog();
} else if (selected_items.size() == 1) {
Media* item = project_parent->item_to_media(selected_items.at(0));
switch (item->get_type()) {
case MEDIA_TYPE_FOOTAGE:
panel_footage_viewer->set_media(get_type_from_tree(item), get_media_from_tree(item));
panel_footage_viewer->set_media(item);
panel_footage_viewer->setFocus();
break;
case MEDIA_TYPE_SEQUENCE:
undo_stack.push(new ChangeSequenceAction(get_sequence_from_tree(item)));
undo_stack.push(new ChangeSequenceAction(item->to_sequence()));
break;
}
}
@@ -211,7 +218,7 @@ void SourceTable::dragEnterEvent(QDragEnterEvent *event) {
if (event->mimeData()->hasUrls()) {
event->acceptProposedAction();
} else {
QTreeWidget::dragEnterEvent(event);
QTreeView::dragEnterEvent(event);
}
}
@@ -219,13 +226,14 @@ void SourceTable::dragMoveEvent(QDragMoveEvent *event) {
if (event->mimeData()->hasUrls()) {
event->acceptProposedAction();
} else {
QTreeWidget::dragMoveEvent(event);
QTreeView::dragMoveEvent(event);
}
}
void SourceTable::dropEvent(QDropEvent* event) {
const QMimeData* mimeData = event->mimeData();
QTreeWidgetItem* drop_item = itemAt(event->pos());
const QModelIndex& drop_item = indexAt(event->pos());
Media* m = project_parent->item_to_media(drop_item);
if (mimeData->hasUrls()) {
// drag files in from outside
QList<QUrl> urls = mimeData->urls();
@@ -236,25 +244,25 @@ void SourceTable::dropEvent(QDropEvent* event) {
}
bool replace = false;
if (urls.size() == 1
&& drop_item != NULL
&& get_type_from_tree(drop_item) == MEDIA_TYPE_FOOTAGE
&& drop_item.isValid()
&& m->get_type() == MEDIA_TYPE_FOOTAGE
&& !QFileInfo(paths.at(0)).isDir()
&& config.drop_on_media_to_replace
&& QMessageBox::question(this, "Replace Media", "You dropped a file onto '" + drop_item->text(0) + "'. Would you like to replace it with the dropped file?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
&& QMessageBox::question(this, "Replace Media", "You dropped a file onto '" + m->get_name() + "'. Would you like to replace it with the dropped file?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
replace = true;
panel_project->replace_media(drop_item, paths.at(0));
project_parent->replace_media(m, paths.at(0));
}
if (!replace) {
QTreeWidgetItem* parent = NULL;
if (drop_item != NULL) {
if (get_type_from_tree(drop_item) == MEDIA_TYPE_FOLDER) {
parent = drop_item;
QModelIndex parent;
if (drop_item.isValid()) {
if (m->get_type() == MEDIA_TYPE_FOLDER) {
parent = drop_item;
} else {
parent = drop_item->parent();
parent = drop_item.parent();
}
}
if (parent != NULL) parent->setExpanded(true);
panel_project->process_file_list(false, paths, parent, NULL);
if (parent.isValid()) setExpanded(parent, true);
project_parent->process_file_list(paths, false, NULL, panel_project->item_to_media(parent));
}
}
event->acceptProposedAction();
@@ -263,24 +271,26 @@ void SourceTable::dropEvent(QDropEvent* event) {
// dragging files within project
// if we dragged to the root OR dragged to a folder
if (drop_item == NULL || (drop_item != NULL && get_type_from_tree(drop_item) == MEDIA_TYPE_FOLDER)) {
QVector<QTreeWidgetItem*> move_items;
QList<QTreeWidgetItem*> selected_items = selectedItems();
if (!drop_item.isValid() || (drop_item.isValid() && m->get_type() == MEDIA_TYPE_FOLDER)) {
QVector<Media*> move_items;
QModelIndexList selected_items = selectionModel()->selectedRows();
for (int i=0;i<selected_items.size();i++) {
QTreeWidgetItem* s = selected_items.at(i);
if (s->parent() != drop_item && s != drop_item) {
const QModelIndex& item = selected_items.at(i);
const QModelIndex& parent = item.parent();
Media* s = project_parent->item_to_media(item);
if (parent != drop_item && item != drop_item) {
bool ignore = false;
if (s->parent() != NULL) {
if (parent.isValid()) {
// if child belongs to a selected parent, assume the user is just moving the parent and ignore the child
QTreeWidgetItem* par = s->parent();
while (par != NULL && !ignore) {
QModelIndex par = parent;
while (par.isValid() && !ignore) {
for (int j=0;j<selected_items.size();j++) {
if (par == selected_items.at(j)) {
ignore = true;
break;
}
}
par = par->parent();
par = par.parent();
}
}
if (!ignore) {
@@ -290,7 +300,7 @@ void SourceTable::dropEvent(QDropEvent* event) {
}
if (move_items.size() > 0) {
MediaMove* mm = new MediaMove(this);
mm->to = drop_item;
mm->to = m;
mm->items = move_items;
undo_stack.push(mm);
}
+8 -6
View File
@@ -1,17 +1,19 @@
#ifndef SOURCETABLE_H
#define SOURCETABLE_H
#include <QTreeWidget>
#include <QTreeView>
#include <QTimer>
#include <QUndoCommand>
class Project;
class Media;
class SourceTable : public QTreeWidget
class SourceTable : public QTreeView
{
Q_OBJECT
public:
SourceTable(QWidget* parent = 0);
Project* project_parent;
protected:
void mousePressEvent(QMouseEvent*);
void mouseDoubleClickEvent(QMouseEvent *event);
@@ -20,13 +22,13 @@ protected:
void dropEvent(QDropEvent *event);
private:
QTimer rename_timer;
QTreeWidgetItem* editing_item;
QString editing_item_name;
Media* editing_item;
QModelIndex editing_index;
private slots:
void rename_interval();
void item_click(QTreeWidgetItem* item, int column);
void item_click(const QModelIndex& index);
void stop_rename_timer();
void item_renamed(QTreeWidgetItem *item);
void item_renamed(Media *item);
void show_context_menu();
void create_seq_from_selected();
void reveal_in_browser();
+36 -2
View File
@@ -1,5 +1,6 @@
#include "timelineheader.h"
#include "mainwindow.h"
#include "panels/panels.h"
#include "panels/timeline.h"
#include "project/sequence.h"
@@ -10,8 +11,10 @@
#include <QPainter>
#include <QMouseEvent>
#include <QScrollArea>
#include <QScrollBar>
#include <QtMath>
#include <QMenu>
#include <QAction>
#define CLICK_RANGE 5
#define PLAYHEAD_SIZE 6
@@ -19,6 +22,16 @@
#define SUBLINE_MIN_PADDING 50 // TODO play with this
#define MARKER_SIZE 4
bool center_scroll_to_playhead(QScrollBar* bar, double zoom, long playhead) {
// returns true is the scroll was changed, false if not
int target_scroll = qMin(bar->maximum(), qMax(0, getScreenPointFromFrame(zoom, playhead)-(bar->width()>>1)));
if (target_scroll == bar->value()) {
return false;
}
bar->setValue(target_scroll);
return true;
}
TimelineHeader::TimelineHeader(QWidget *parent) :
QWidget(parent),
snapping(true),
@@ -35,6 +48,9 @@ TimelineHeader::TimelineHeader(QWidget *parent) :
setMouseTracking(true);
setFocusPolicy(Qt::ClickFocus);
show_text(true);
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(show_context_menu(const QPoint &)));
}
void TimelineHeader::set_scroll(int s) {
@@ -87,6 +103,10 @@ void TimelineHeader::set_out_point(long new_out) {
update_parents();
}
void TimelineHeader::set_scrollbar_max(QScrollBar* bar, long sequence_end_frame, int offset) {
bar->setMaximum(qMax(0, getScreenPointFromFrame(zoom, sequence_end_frame) - offset));
}
void TimelineHeader::show_text(bool enable) {
text_enabled = enable;
if (enable) {
@@ -255,7 +275,11 @@ void TimelineHeader::update_parents() {
void TimelineHeader::update_zoom(double z) {
zoom = z;
update();
update();
}
double TimelineHeader::get_zoom() {
return zoom;
}
void TimelineHeader::delete_markers() {
@@ -376,3 +400,13 @@ void TimelineHeader::paintEvent(QPaintEvent*) {
p.fillPath(path, Qt::red);
}
}
void TimelineHeader::show_context_menu(const QPoint &pos) {
QMenu contextMenu(tr("Context menu"), this);
QAction clear_in_out("Clear In/Out Points", this);
if (!viewer->seq->using_workarea) clear_in_out.setEnabled(false);
connect(&clear_in_out, SIGNAL(triggered()), mainWindow, SLOT(on_actionClear_In_Out_triggered()));
contextMenu.addAction(&clear_in_out);
contextMenu.exec(mapToGlobal(pos));
}
+6 -1
View File
@@ -3,8 +3,10 @@
#include <QWidget>
#include <QFontMetrics>
class QScrollArea;
class Viewer;
class QScrollBar;
bool center_scroll_to_playhead(QScrollBar* bar, double zoom, long playhead);
class TimelineHeader : public QWidget
{
@@ -20,11 +22,14 @@ public:
void show_text(bool enable);
void update_zoom(double z);
double get_zoom();
void delete_markers();
void set_scrollbar_max(QScrollBar* bar, long sequence_end_frame, int offset);
public slots:
void set_scroll(int);
void set_visible_in(long i);
void show_context_menu(const QPoint &pos);
protected:
void paintEvent(QPaintEvent*);
+73 -52
View File
@@ -8,7 +8,7 @@
#include "project/clip.h"
#include "panels/project.h"
#include "panels/timeline.h"
#include "io/media.h"
#include "project/footage.h"
#include "ui/sourcetable.h"
#include "panels/effectcontrols.h"
#include "panels/viewer.h"
@@ -16,6 +16,8 @@
#include "ui_timeline.h"
#include "mainwindow.h"
#include "ui/viewerwidget.h"
#include "dialogs/stabilizerdialog.h"
#include "project/media.h"
#include "debug.h"
#include "project/effect.h"
@@ -149,6 +151,23 @@ void TimelineWidget::show_context_menu(const QPoint& pos) {
QAction* nestAction = menu.addAction("&Nest");
connect(nestAction, SIGNAL(triggered(bool)), mainWindow, SLOT(on_actionNest_triggered()));
// stabilizer option
int video_clip_count = 0;
bool all_video_is_footage = true;
for (int i=0;i<selected_clips.size();i++) {
if (selected_clips.at(i)->track < 0) {
video_clip_count++;
if (selected_clips.at(i)->media == NULL
|| selected_clips.at(i)->media->get_type() != MEDIA_TYPE_FOOTAGE) {
all_video_is_footage = false;
}
}
}
if (video_clip_count == 1 && all_video_is_footage) {
QAction* stabilizerAction = menu.addAction("S&tabilizer");
connect(stabilizerAction, SIGNAL(triggered(bool)), this, SLOT(show_stabilizer_diag()));
}
// set autoscale arbitrarily to the first selected clip
autoscaleAction->setChecked(selected_clips.at(0)->autoscale);
@@ -228,7 +247,12 @@ void TimelineWidget::rename_clip() {
undo_stack.push(rcc);
update_ui(true);
}
}
}
}
void TimelineWidget::show_stabilizer_diag() {
StabilizerDialog sd;
sd.exec();
}
bool same_sign(int a, int b) {
@@ -238,17 +262,14 @@ bool same_sign(int a, int b) {
void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
bool import_init = false;
QVector<void*> media_list;
QVector<int> type_list;
QVector<Media*> media_list;
panel_timeline->importing_files = false;
if (event->source() == panel_project->source_table) {
QList<QTreeWidgetItem*> items = panel_project->source_table->selectedItems();
media_list.resize(items.size());
type_list.resize(items.size());
for (int i=0;i<items.size();i++) {
type_list[i] = get_type_from_tree(items.at(i));
media_list[i] = get_media_from_tree(items.at(i));
QModelIndexList items = panel_project->source_table->selectionModel()->selectedRows();
media_list.resize(items.size());
for (int i=0;i<items.size();i++) {
media_list[i] = panel_project->item_to_media(items.at(i));
}
import_init = true;
}
@@ -256,13 +277,7 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
if (event->source() == panel_footage_viewer->viewer_widget) {
Sequence* proposed_seq = panel_footage_viewer->seq;
if (proposed_seq != sequence) { // don't allow nesting the same sequence
if (proposed_seq->wrapper_sequence) {
type_list.append(MEDIA_TYPE_FOOTAGE);
media_list.append(proposed_seq->clips.at(0)->media);
} else {
type_list.append(MEDIA_TYPE_SEQUENCE);
media_list.append(proposed_seq);
}
media_list.append(panel_footage_viewer->media);
import_init = true;
}
}
@@ -276,17 +291,17 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
file_list.append(urls.at(i).toLocalFile());
}
panel_project->process_file_list(false, file_list, NULL, NULL);
panel_project->process_file_list(file_list);
for (int i=0;i<panel_project->last_imported_media.size();i++) {
// waits for media to have a duration
// TODO would be much nicer if this was multithreaded
panel_project->last_imported_media.at(i)->ready_lock.lock();
panel_project->last_imported_media.at(i)->ready_lock.unlock();
Footage* f = panel_project->last_imported_media.at(i)->to_footage();
f->ready_lock.lock();
f->ready_lock.unlock();
if (panel_project->last_imported_media.at(i)->ready) {
media_list.append(panel_project->last_imported_media.at(i));
type_list.append(MEDIA_TYPE_FOOTAGE);
if (f->ready) {
media_list.append(panel_project->last_imported_media.at(i));
}
}
@@ -299,8 +314,8 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
}
}
if (import_init) {
event->accept();
if (import_init) {
event->acceptProposedAction();
long entry_point;
Sequence* seq = sequence;
@@ -309,7 +324,7 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
// if no sequence, we're going to create a new one using the clips as a reference
entry_point = 0;
self_created_sequence = create_sequence_from_media(media_list, type_list);
self_created_sequence = create_sequence_from_media(media_list);
seq = self_created_sequence;
} else {
entry_point = panel_timeline->getTimelineFrameFromScreenPoint(event->pos().x());
@@ -317,18 +332,22 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
panel_timeline->drag_track_start = (bottom_align) ? -1 : 0;
}
panel_timeline->create_ghosts_from_media(seq, entry_point, media_list, type_list);
panel_timeline->create_ghosts_from_media(seq, entry_point, media_list);
panel_timeline->importing = true;
}
}
}
void TimelineWidget::dragMoveEvent(QDragMoveEvent *event) {
if (sequence != NULL && panel_timeline->importing) {
QPoint pos = event->pos();
update_ghosts(pos, event->keyboardModifiers() & Qt::ShiftModifier);
panel_timeline->move_insert = ((event->keyboardModifiers() & Qt::ControlModifier) && (panel_timeline->tool == TIMELINE_TOOL_POINTER || panel_timeline->importing));
update_ui(false);
if (panel_timeline->importing) {
event->acceptProposedAction();
if (sequence != NULL) {
QPoint pos = event->pos();
update_ghosts(pos, event->keyboardModifiers() & Qt::ShiftModifier);
panel_timeline->move_insert = ((event->keyboardModifiers() & Qt::ControlModifier) && (panel_timeline->tool == TIMELINE_TOOL_POINTER || panel_timeline->importing));
update_ui(false);
}
}
}
@@ -350,7 +369,8 @@ void TimelineWidget::wheelEvent(QWheelEvent *event) {
}
}
void TimelineWidget::dragLeaveEvent(QDragLeaveEvent*) {
void TimelineWidget::dragLeaveEvent(QDragLeaveEvent* event) {
event->accept();
if (panel_timeline->importing) {
if (panel_timeline->importing_files) {
undo_stack.undo();
@@ -362,6 +382,7 @@ void TimelineWidget::dragLeaveEvent(QDragLeaveEvent*) {
}
if (self_created_sequence != NULL) {
delete self_created_sequence;
self_created_sequence = NULL;
}
}
@@ -464,7 +485,7 @@ void insert_clips(ComboAction* ca) {
void TimelineWidget::dropEvent(QDropEvent* event) {
if (panel_timeline->importing && panel_timeline->ghosts.size() > 0) {
event->accept();
event->acceptProposedAction();
ComboAction* ca = new ComboAction();
@@ -773,7 +794,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
if (c->track < 0) {
// default video effects (before custom effects)
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_TRANSFORM, EFFECT_TYPE_EFFECT)));
c->media_type = MEDIA_TYPE_SOLID;
//c->media_type = MEDIA_TYPE_SOLID;
}
switch (panel_timeline->creating_object) {
@@ -807,7 +828,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
// default audio effects (after custom effects)
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_VOLUME, EFFECT_TYPE_EFFECT)));
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_PAN, EFFECT_TYPE_EFFECT)));
c->media_type = MEDIA_TYPE_TONE;
//c->media_type = MEDIA_TYPE_TONE;
}
push_undo = true;
@@ -1227,17 +1248,17 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
Clip* c = NULL;
if (g.clip != -1) c = sequence->clips.at(g.clip);
MediaStream* ms = NULL;
if (g.clip != -1 && c->media_type == MEDIA_TYPE_FOOTAGE) {
ms = static_cast<Media*>(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream);
FootageStream* ms = NULL;
if (g.clip != -1 && c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream);
}
// validate ghosts for trimming
if (panel_timeline->creating) {
// i feel like we might need something here but we haven't so far?
} else if (panel_timeline->tool == TIMELINE_TOOL_SLIP) {
if (c->media_type == MEDIA_TYPE_SEQUENCE
|| (c->media_type == MEDIA_TYPE_FOOTAGE && !static_cast<Media*>(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream)->infinite_length)) {
if (c->media->get_type() == MEDIA_TYPE_SEQUENCE
|| (ms != NULL && !ms->infinite_length)) {
// prevent slip moving a clip below 0 clip_in
validator = g.old_clip_in - frame_diff;
if (validator < 0) frame_diff += validator;
@@ -1257,7 +1278,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
if (validator < 0) frame_diff -= validator;
// prevent clip_in from going below 0
if (c->media_type == MEDIA_TYPE_SEQUENCE
if (c->media->get_type() == MEDIA_TYPE_SEQUENCE
|| (ms != NULL && !ms->infinite_length)) {
validator = g.old_clip_in + frame_diff;
if (validator < 0) frame_diff -= validator;
@@ -1268,7 +1289,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
if (validator < 1) frame_diff += (1 - validator);
// prevent clip length exceeding media length
if (c->media_type == MEDIA_TYPE_SEQUENCE
if ((c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE)
|| (ms != NULL && !ms->infinite_length)) {
validator = g.old_clip_in + g.ghost_length + frame_diff;
if (validator > g.media_length) frame_diff -= validator - g.media_length;
@@ -1349,14 +1370,14 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
if (validator > 0) frame_diff -= validator;
} else {
// prevent clip_in from going below 0
if (c->media_type == MEDIA_TYPE_SEQUENCE
if (c->media->get_type() == MEDIA_TYPE_SEQUENCE
|| (ms != NULL && !ms->infinite_length)) {
validator = g.old_clip_in + frame_diff;
if (validator < 0) frame_diff -= validator;
}
// prevent clip length exceeding media length
if (c->media_type == MEDIA_TYPE_SEQUENCE
if (c->media->get_type() == MEDIA_TYPE_SEQUENCE
|| (ms != NULL && !ms->infinite_length)) {
validator = g.old_clip_in + g.ghost_length + frame_diff;
if (validator > g.media_length) frame_diff -= validator - g.media_length;
@@ -2049,7 +2070,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
g.in = g.old_in = g.out = g.old_out = (panel_timeline->transition_tool_type == TA_OPENING_TRANSITION) ? c->timeline_in : c->timeline_out;
g.track = c->track;
g.clip = panel_timeline->transition_tool_pre_clip;
g.media_type = panel_timeline->transition_tool_type;
g.media_stream = panel_timeline->transition_tool_type;
g.trimming = false;
panel_timeline->ghosts.append(g);
@@ -2097,7 +2118,7 @@ int color_brightness(int r, int g, int b) {
return (0.2126*r + 0.7152*g + 0.0722*b);
}
void draw_waveform(Clip* clip, MediaStream* ms, long media_length, QPainter *p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom) {
void draw_waveform(Clip* clip, FootageStream* ms, long media_length, QPainter *p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom) {
int divider = ms->audio_channels*2;
int channel_height = clip_rect.height()/ms->audio_channels;
@@ -2225,11 +2246,11 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
int thumb_x = clip_rect.x() + 1;
if (clip->media_type == MEDIA_TYPE_FOOTAGE) {
if (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_FOOTAGE) {
bool draw_checkerboard = false;
QRect checkerboard_rect(clip_rect);
Media* m = static_cast<Media*>(clip->media);
MediaStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream);
Footage* m = clip->media->to_footage();
FootageStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream);
if (ms == NULL) {
draw_checkerboard = true;
} else if (ms->preview_done) {
@@ -2641,5 +2662,5 @@ void TimelineWidget::setScroll(int s) {
}
void TimelineWidget::reveal_media() {
panel_project->reveal_media(rc_reveal_media);
panel_project->reveal_media(rc_reveal_media);
}
+3 -2
View File
@@ -13,7 +13,7 @@
struct Sequence;
struct Clip;
struct MediaStream;
struct FootageStream;
class Timeline;
class TimelineAction;
class QScrollBar;
@@ -21,7 +21,7 @@ class SetSelectionsCommand;
class QPainter;
bool same_sign(int a, int b);
void draw_waveform(Clip* clip, MediaStream* ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom);
void draw_waveform(Clip* clip, FootageStream* ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom);
class TimelineWidget : public QWidget {
Q_OBJECT
@@ -85,6 +85,7 @@ private slots:
void toggle_autoscale();
void tooltip_timer_timeout();
void rename_clip();
void show_stabilizer_diag();
};
#endif // TIMELINEWIDGET_H
+77 -20
View File
@@ -2,36 +2,93 @@
#include <QWidget>
#include <QResizeEvent>
#include <QScrollBar>
#include <QVBoxLayout>
#include "viewerwidget.h"
#include "panels/viewer.h"
#include "project/sequence.h"
#include "debug.h"
// enforces aspect ratio
ViewerContainer::ViewerContainer(QWidget *parent) :
QWidget(parent),
aspect_ratio(1),
QScrollArea(parent),
fit(true),
child(NULL)
{}
{
setFrameShadow(QFrame::Plain);
setFrameShape(QFrame::NoFrame);
area = new QWidget(this);
area->move(0, 0);
setWidget(area);
child = new ViewerWidget(area);
child->container = this;
}
ViewerContainer::~ViewerContainer() {
delete area;
}
void ViewerContainer::dragScrollPress(const QPoint &p) {
drag_start_x = p.x();
drag_start_y = p.y();
horiz_start = horizontalScrollBar()->value();
vert_start = verticalScrollBar()->value();
}
void ViewerContainer::dragScrollMove(const QPoint &p) {
int true_x = p.x() + (horiz_start - horizontalScrollBar()->value());
int true_y = p.y() + (vert_start - verticalScrollBar()->value());
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + (drag_start_x - true_x));
verticalScrollBar()->setValue(verticalScrollBar()->value() + (drag_start_y - true_y));
drag_start_x = true_x;
drag_start_y = true_y;
}
void ViewerContainer::adjust() {
if (child != NULL) {
QSize widget_size = size();
int widget_x = 0;
int widget_y = 0;
int widget_width = widget_size.width();
int widget_height = widget_size.height();
float widget_ar = (float) widget_width /(float) widget_height;
if (viewer->seq != NULL) {
if (child->waveform) {
child->move(0, 0);
child->resize(size());
} else if (fit) {
double aspect_ratio = double(viewer->seq->width)/double(viewer->seq->height);
bool widget_is_larger_than_sequence = widget_ar > aspect_ratio;
int widget_x = 0;
int widget_y = 0;
int widget_width = width();
int widget_height = height();
float widget_ar = (float) widget_width /(float) widget_height;
if (widget_is_larger_than_sequence) {
widget_width = widget_height * aspect_ratio;
widget_x = (widget_size.width() / 2) - (widget_width / 2);
} else {
widget_height = widget_width / aspect_ratio;
widget_y = (widget_size.height() / 2) - (widget_height / 2);
}
bool widget_is_wider_than_sequence = widget_ar > aspect_ratio;
child->move(widget_x, widget_y);
child->resize(widget_width, widget_height);
if (widget_is_wider_than_sequence) {
widget_width = widget_height * aspect_ratio;
widget_x = (width() / 2) - (widget_width / 2);
} else {
widget_height = widget_width / aspect_ratio;
widget_y = (height() / 2) - (widget_height / 2);
}
child->move(widget_x, widget_y);
child->resize(widget_width, widget_height);
} else {
int zoomed_width = double(viewer->seq->width)*zoom;
int zoomed_height = double(viewer->seq->height)*zoom;
int zoomed_x = 0;
int zoomed_y = 0;
if (zoomed_width < width()) zoomed_x = (width()>>1)-(zoomed_width>>1);
if (zoomed_height < height()) zoomed_y = (height()>>1)-(zoomed_height>>1);
child->move(zoomed_x, zoomed_y);
child->resize(zoomed_width, zoomed_height);
}
}
area->resize(qMax(width(), child->width()), qMax(height(), child->height()));
}
void ViewerContainer::resizeEvent(QResizeEvent *event) {
+21 -4
View File
@@ -1,15 +1,25 @@
#ifndef VIEWERCONTAINER_H
#define VIEWERCONTAINER_H
#include <QWidget>
#include <QScrollArea>
class Viewer;
class ViewerWidget;
class ViewerContainer : public QWidget
class ViewerContainer : public QScrollArea
{
Q_OBJECT
public:
explicit ViewerContainer(QWidget *parent = 0);
float aspect_ratio;
QWidget* child;
~ViewerContainer();
bool fit;
double zoom;
void dragScrollPress(const QPoint&);
void dragScrollMove(const QPoint&);
Viewer* viewer;
ViewerWidget* child;
void adjust();
protected:
@@ -18,6 +28,13 @@ protected:
signals:
public slots:
private:
QWidget* area;
int drag_start_x;
int drag_start_y;
int horiz_start;
int vert_start;
};
#endif // VIEWERCONTAINER_H
+143 -80
View File
@@ -10,7 +10,7 @@
#include "project/transition.h"
#include "playback/playback.h"
#include "playback/audio.h"
#include "io/media.h"
#include "project/footage.h"
#include "ui_timeline.h"
#include "playback/cacher.h"
#include "io/config.h"
@@ -18,6 +18,9 @@
#include "io/math.h"
#include "ui/collapsiblewidget.h"
#include "project/undo.h"
#include "project/media.h"
#include "ui/viewercontainer.h"
#include "io/avtogl.h"
#include <QPainter>
#include <QAudioOutput>
@@ -31,6 +34,7 @@
#include <QOffscreenSurface>
#include <QFileDialog>
#include <QPolygon>
#include <QDesktopWidget>
extern "C" {
#include <libavformat/avformat.h>
@@ -43,7 +47,9 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
default_fbo(NULL),
waveform(false),
dragging(false),
selected_gizmo(NULL)
selected_gizmo(NULL),
waveform_zoom(1.0),
waveform_scroll(0)
{
setMouseTracking(true);
setFocusPolicy(Qt::ClickFocus);
@@ -52,7 +58,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
format.setDepthBufferSize(24);
setFormat(format);
// error handler - retries after 500ms if we couldn't get the entire image
// error handler - retries after 50ms if we couldn't get the entire image
retry_timer.setInterval(50);
connect(&retry_timer, SIGNAL(timeout()), this, SLOT(retry()));
@@ -62,13 +68,20 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
void ViewerWidget::delete_function() {
// destroy all textures as well
if (viewer->seq != NULL) {
if (viewer->seq != NULL) {
makeCurrent();
closeActiveClips(viewer->seq, true);
doneCurrent();
}
}
void ViewerWidget::set_waveform_scroll(int s) {
if (waveform) {
waveform_scroll = s;
update();
}
}
void ViewerWidget::show_context_menu() {
QMenu menu(this);
@@ -126,7 +139,7 @@ void ViewerWidget::initializeGL() {
connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(delete_function()), Qt::DirectConnection);
retry_timer.start();
retry_timer.start();
}
//void ViewerWidget::resizeGL(int w, int h)
@@ -141,11 +154,7 @@ void ViewerWidget::paintEvent(QPaintEvent *e) {
}
void ViewerWidget::seek_from_click(int x) {
viewer->seek(getFrameFromScreenPoint((double) width() / (double) waveform_clip->timeline_out, x));
}
double get_timecode(Clip* c, long playhead) {
return ((double)(playhead-c->get_timeline_in_with_transition()+c->get_clip_in_with_transition())/(double)c->sequence->frame_rate);
viewer->seek(getFrameFromScreenPoint(waveform_zoom, x+waveform_scroll));
}
EffectGizmo* ViewerWidget::get_gizmo_from_mouse(int x, int y) {
@@ -153,6 +162,7 @@ EffectGizmo* ViewerWidget::get_gizmo_from_mouse(int x, int y) {
double multiplier = (double) viewer->seq->width / (double) width();
QPoint mouse_pos(qRound(x*multiplier), qRound(y*multiplier));
int dot_size = 2 * qRound(GIZMO_DOT_SIZE * multiplier);
int target_size = 2 * qRound(GIZMO_TARGET_SIZE * multiplier);
for (int i=0;i<gizmos->gizmo_count();i++) {
EffectGizmo* g = gizmos->gizmo(i);
@@ -170,6 +180,14 @@ EffectGizmo* ViewerWidget::get_gizmo_from_mouse(int x, int y) {
return g;
}
break;
case GIZMO_TYPE_TARGET:
if (mouse_pos.x() > g->screen_pos[0].x() - target_size
&& mouse_pos.y() > g->screen_pos[0].y() - target_size
&& mouse_pos.x() < g->screen_pos[0].x() + target_size
&& mouse_pos.y() < g->screen_pos[0].y() + target_size) {
return g;
}
break;
}
}
@@ -199,6 +217,8 @@ void ViewerWidget::move_gizmos(QMouseEvent *event, bool done) {
void ViewerWidget::mousePressEvent(QMouseEvent* event) {
if (waveform) {
seek_from_click(event->x());
} else if (event->buttons() & Qt::MiddleButton) {
container->dragScrollPress(event->pos());
} else {
drag_start_x = event->pos().x();
drag_start_y = event->pos().y();
@@ -216,9 +236,11 @@ void ViewerWidget::mousePressEvent(QMouseEvent* event) {
}
void ViewerWidget::mouseMoveEvent(QMouseEvent* event) {
if (dragging) {
if (dragging) {
if (waveform) {
seek_from_click(event->x());
} else if (event->buttons() & Qt::MiddleButton) {
container->dragScrollMove(event->pos());
} else if (gizmos == NULL) {
QDrag* drag = new QDrag(this);
QMimeData* mimeData = new QMimeData;
@@ -321,6 +343,14 @@ GLuint ViewerWidget::draw_clip(QOpenGLFramebufferObject* fbo, GLuint texture, bo
fbo->bind();
if (clear) glClear(GL_COLOR_BUFFER_BIT);
// get current blend mode
GLint src_rgb, src_alpha, dst_rgb, dst_alpha;
glGetIntegerv(GL_BLEND_SRC_RGB, &src_rgb);
glGetIntegerv(GL_BLEND_SRC_ALPHA, &src_alpha);
glGetIntegerv(GL_BLEND_DST_RGB, &dst_rgb);
glGetIntegerv(GL_BLEND_DST_ALPHA, &dst_alpha);
GL_DEFAULT_BLEND
glBindTexture(GL_TEXTURE_2D, texture);
@@ -337,7 +367,11 @@ GLuint ViewerWidget::draw_clip(QOpenGLFramebufferObject* fbo, GLuint texture, bo
glBindTexture(GL_TEXTURE_2D, 0);
fbo->release();
if (default_fbo != NULL) default_fbo->bind();
// restore previous blendFunc
glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha);
if (default_fbo != NULL) default_fbo->bind();
glPopMatrix();
return fbo->texture();
@@ -378,7 +412,7 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
if (!nests.isEmpty()) {
for (int i=0;i<nests.size();i++) {
s = static_cast<Sequence*>(nests.at(i)->media);
s = nests.at(i)->media->to_sequence();
playhead += nests.at(i)->clip_in - nests.at(i)->get_timeline_in_with_transition();
playhead = refactor_frame_number(playhead, nests.at(i)->sequence->frame_rate, s->frame_rate);
}
@@ -402,13 +436,11 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
if (!(!nests.isEmpty() && !same_sign(c->track, nests.last()->track))) {
bool clip_is_active = false;
switch (c->media_type) {
case MEDIA_TYPE_FOOTAGE:
{
Media* m = static_cast<Media*>(c->media);
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
Footage* m = c->media->to_footage();
if (!m->invalid && !(c->track >= 0 && !is_audio_device_set())) {
if (m->ready) {
MediaStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
if (ms != 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
@@ -421,23 +453,18 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
close_clip(c);
}
} else {
dout << "[WARNING] Media was not ready, retrying...";
//dout << "[WARNING] Media '" + m->name + "' was not ready, retrying...";
texture_failed = true;
}
}
}
break;
case MEDIA_TYPE_SEQUENCE:
case MEDIA_TYPE_SOLID:
case MEDIA_TYPE_TONE:
if (is_clip_active(c, playhead)) {
if (!c->open) open_clip(c, !rendering);
clip_is_active = true;
} else if (c->open) {
close_clip(c);
}
break;
}
} else {
if (is_clip_active(c, playhead)) {
if (!c->open) open_clip(c, !rendering);
clip_is_active = true;
} else if (c->open) {
close_clip(c);
}
}
if (clip_is_active) {
bool added = false;
for (int j=0;j<current_clips.size();j++) {
@@ -469,7 +496,7 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
Clip* c = current_clips.at(i);
if (c->media_type == MEDIA_TYPE_FOOTAGE && !c->finished_opening) {
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE && !c->finished_opening) {
dout << "[WARNING] Tried to display clip" << i << "but it's closed";
texture_failed = true;
} else {
@@ -478,23 +505,28 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
int video_width = c->getWidth();
int video_height = c->getHeight();
if (c->media_type == MEDIA_TYPE_FOOTAGE) {
// set up opengl texture
if (c->texture == NULL) {
c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
c->texture->setSize(c->stream->codecpar->width, c->stream->codecpar->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);
}
get_clip_frame(c, playhead);
textureID = c->texture->textureId();
} else if (c->media_type == MEDIA_TYPE_SEQUENCE) {
textureID = -1;
}
if (c->media != NULL) {
switch (c->media->get_type()) {
case MEDIA_TYPE_FOOTAGE:
// set up opengl texture
if (c->texture == NULL) {
c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
c->texture->setSize(c->stream->codecpar->width, c->stream->codecpar->height);
c->texture->setFormat(get_gl_tex_fmt_from_av(c->pix_fmt));
c->texture->setMipLevels(c->texture->maximumMipLevels());
c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
c->texture->allocateStorage(get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8);
}
get_clip_frame(c, playhead);
textureID = c->texture->textureId();
break;
case MEDIA_TYPE_SEQUENCE:
textureID = -1;
break;
}
}
if (textureID == 0 && c->media_type != MEDIA_TYPE_SOLID) {
if (textureID == 0 && c->media != NULL) {
dout << "[WARNING] Texture hasn't been created yet";
texture_failed = true;
} else if (playhead >= c->get_timeline_in_with_transition()) {
@@ -519,21 +551,22 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
glViewport(0, 0, video_width, video_height);
// for nested sequences
if (c->media_type == MEDIA_TYPE_SEQUENCE) {
nests.append(c);
textureID = compose_sequence(nests, render_audio);
nests.removeLast();
fbo_switcher = true;
}
GLuint composite_texture;
GLuint composite_texture;
if (c->media_type == MEDIA_TYPE_SOLID) {
if (c->media == NULL) {
c->fbo[fbo_switcher]->bind();
glClear(GL_COLOR_BUFFER_BIT);
c->fbo[fbo_switcher]->release();
composite_texture = c->fbo[fbo_switcher]->texture();
} else {
// for nested sequences
if (c->media->get_type()== MEDIA_TYPE_SEQUENCE) {
nests.append(c);
textureID = compose_sequence(nests, render_audio);
nests.removeLast();
fbo_switcher = true;
}
composite_texture = draw_clip(c->fbo[fbo_switcher], textureID, true);
}
@@ -604,7 +637,17 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
} else if (rendering) {
glViewport(0, 0, s->width, s->height);
} else {
glViewport(0, 0, width(), height());
int widget_width = width();
int widget_height = height();
QString qt_scale_factor = QString(qgetenv("QT_SCALE_FACTOR"));
if (!qt_scale_factor.isEmpty()) {
double scale = qt_scale_factor.toDouble();
widget_width *= scale;
widget_height *= scale;
}
glViewport(0, 0, widget_width, widget_height);
}
glBindTexture(GL_TEXTURE_2D, composite_texture);
@@ -686,21 +729,17 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
}
} else {
if (render_audio || (config.enable_audio_scrubbing && audio_scrub)) {
switch (c->media_type) {
case MEDIA_TYPE_FOOTAGE:
case MEDIA_TYPE_TONE:
if (c->lock.tryLock()) {
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
nests.append(c);
compose_sequence(nests, render_audio);
nests.removeLast();
} else {
if (c->lock.tryLock()) {
// clip is not caching, start caching audio
cache_clip(c, playhead, c->audio_reset, !render_audio, nests);
c->lock.unlock();
}
break;
case MEDIA_TYPE_SEQUENCE:
nests.append(c);
compose_sequence(nests, render_audio);
nests.removeLast();
break;
}
cache_clip(c, playhead, c->audio_reset, !render_audio, nests);
c->lock.unlock();
}
}
}
// visually update all the keyframe values
@@ -736,6 +775,7 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
void ViewerWidget::paintGL() {
drawn_gizmos = false;
force_quit = false;
if (viewer->seq != NULL) {
gizmos = NULL;
@@ -764,26 +804,27 @@ void ViewerWidget::paintGL() {
compose_sequence(nests, render_audio);
if (waveform) {
double waveform_zoom = (double) waveform_ms->audio_preview.size() / (double) width();
double timeline_zoom = (double) width() / (double) waveform_clip->timeline_out;
QPainter p(this);
if (viewer->seq->using_workarea) {
int in_x = getScreenPointFromFrame(timeline_zoom, viewer->seq->workarea_in);
int out_x = getScreenPointFromFrame(timeline_zoom, viewer->seq->workarea_out);
int in_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->workarea_in) - waveform_scroll;
int out_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->workarea_out) - waveform_scroll;
p.fillRect(QRect(in_x, 0, out_x - in_x, height()), QColor(255, 255, 255, 64));
p.setPen(Qt::white);
p.drawLine(in_x, 0, in_x, height());
p.drawLine(out_x, 0, out_x, height());
}
QRect wr = rect();
wr.setX(wr.x() - waveform_scroll);
p.setPen(Qt::green);
draw_waveform(waveform_clip, waveform_ms, waveform_clip->timeline_out, &p, rect(), 0, width(), waveform_zoom);
draw_waveform(waveform_clip, waveform_ms, waveform_clip->timeline_out, &p, wr, waveform_scroll, width()+waveform_scroll, waveform_zoom);
p.setPen(Qt::red);
int playhead_x = getScreenPointFromFrame(timeline_zoom, viewer->seq->playhead);
int playhead_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->playhead) - waveform_scroll;
p.drawLine(playhead_x, 0, playhead_x, height());
}
if (force_quit) break;
if (texture_failed) {
if (rendering) {
dout << "[INFO] Texture failed - looping";
@@ -802,6 +843,7 @@ void ViewerWidget::paintGL() {
glGetFloatv(GL_CURRENT_COLOR, color);
float dot_size = GIZMO_DOT_SIZE / width() * viewer->seq->width;
float target_size = GIZMO_TARGET_SIZE / width() * viewer->seq->width;
glPushMatrix();
glLoadIdentity();
@@ -829,6 +871,27 @@ void ViewerWidget::paintGL() {
glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y(), gizmo_z);
glEnd();
break;
case GIZMO_TYPE_TARGET: // draw target
glBegin(GL_LINES);
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z);
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z);
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z);
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z);
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z);
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z);
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z);
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z);
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y(), gizmo_z);
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y(), gizmo_z);
glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()-target_size, gizmo_z);
glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()+target_size, gizmo_z);
glEnd();
break;
}
}
glPopMatrix();
+9 -2
View File
@@ -12,10 +12,11 @@
class Viewer;
struct Clip;
struct MediaStream;
struct FootageStream;
class QOpenGLFramebufferObject;
class Effect;
class EffectGizmo;
class ViewerContainer;
struct GLTextureCoords;
class ViewerWidget : public QOpenGLWidget, QOpenGLFunctions
@@ -27,14 +28,20 @@ public:
void paintGL();
void initializeGL();
Viewer* viewer;
ViewerContainer* container;
QOpenGLFramebufferObject* default_fbo;
bool waveform;
Clip* waveform_clip;
MediaStream* waveform_ms;
FootageStream* waveform_ms;
double waveform_zoom;
int waveform_scroll;
bool force_quit;
public slots:
void delete_function();
void set_waveform_scroll(int s);
protected:
void paintEvent(QPaintEvent *e);
// void resizeGL(int w, int h);