Merge pull request #189 from olive-editor/alpha
Merge current alpha to master
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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):");
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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() {
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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/>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+31
-45
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+76
-52
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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 Model–view–viewmodel 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
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -41,6 +41,7 @@ private:
|
||||
QVBoxLayout* layout;
|
||||
QPushButton* collapse_button;
|
||||
QFrame* line;
|
||||
QHBoxLayout* title_bar_layout;
|
||||
|
||||
signals:
|
||||
void deselect_others(QWidget*);
|
||||
|
||||
+3
-3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user