massive rewrite of project data system
This commit is contained in:
+19
-4
@@ -6,25 +6,40 @@
|
||||
#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"
|
||||
|
||||
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);
|
||||
layout->addWidget(bar);
|
||||
|
||||
QPushButton* cancel_button = new QPushButton("Cancel");
|
||||
cancel_button = new QPushButton("Cancel");
|
||||
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(reject()));
|
||||
|
||||
QHBoxLayout* hboxLayout = new QHBoxLayout();
|
||||
hboxLayout = new QHBoxLayout();
|
||||
hboxLayout->addStretch();
|
||||
hboxLayout->addWidget(cancel_button);
|
||||
hboxLayout->addStretch();
|
||||
|
||||
layout->addLayout(hboxLayout);
|
||||
|
||||
LoadThread* lt = new LoadThread(this, autorecovery);
|
||||
QObject::connect(lt, SIGNAL(success()), this, SLOT(thread_done()));
|
||||
lt->start();
|
||||
}
|
||||
|
||||
void LoadDialog::thread_done() {
|
||||
// project_model.update_data();
|
||||
close();
|
||||
}
|
||||
|
||||
+10
-1
@@ -4,13 +4,22 @@
|
||||
#include <QDialog>
|
||||
|
||||
class QProgressBar;
|
||||
class Sequence;
|
||||
class Media;
|
||||
class Footage;
|
||||
class QHBoxLayout;
|
||||
|
||||
class LoadDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LoadDialog(QWidget* parent = 0);
|
||||
LoadDialog(QWidget* parent, bool autorecovery);
|
||||
private slots:
|
||||
void thread_done();
|
||||
private:
|
||||
QProgressBar* bar;
|
||||
QPushButton* cancel_button;
|
||||
QHBoxLayout* hboxLayout;
|
||||
};
|
||||
|
||||
#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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
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,639 @@
|
||||
#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 "ui_project.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) {
|
||||
connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
|
||||
connect(this, SIGNAL(success()), this, SLOT(success_func()));
|
||||
}
|
||||
|
||||
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 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();
|
||||
|
||||
if (tag == "opening" || tag == "closing") {
|
||||
// TODO replace NULL/s with something 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 (tag == "opening") {
|
||||
c->opening_transition = transition_index;
|
||||
} else {
|
||||
c->closing_transition = transition_index;
|
||||
}
|
||||
} else {
|
||||
Effect* e = create_effect(c, meta);
|
||||
e->set_enabled(effect_enabled);
|
||||
e->load(stream);
|
||||
|
||||
c->effects.append(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
stream.readNextStartElement();
|
||||
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 (!(stream.name() == root_search && stream.isEndElement())) {
|
||||
stream.readNext();
|
||||
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);
|
||||
|
||||
if (folder == 0) {
|
||||
project_model.addTopLevelItem(item);
|
||||
} else {
|
||||
find_loaded_folder_by_id(folder)->appendChild(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 (!(stream.name() == child_search && stream.isEndElement()) && !stream.atEnd()) {
|
||||
stream.readNextStartElement();
|
||||
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 (!(stream.name() == "clip" && stream.isEndElement()) && !stream.atEnd()) {
|
||||
stream.readNext();
|
||||
if (stream.isStartElement()) {
|
||||
if (stream.name() == "linked") {
|
||||
while (!(stream.name() == "linked" && stream.isEndElement()) && !stream.atEnd()) {
|
||||
stream.readNext();
|
||||
if (stream.name() == "link" && stream.isStartElement()) {
|
||||
for (int k=0;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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (stream.isStartElement() && (stream.name() == "effect" || stream.name() == "opening" || stream.name() == "closing")) {
|
||||
// "opening" and "closing" are backwards compatibility code
|
||||
load_effect(stream, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s->clips.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Media* m = panel_project->new_sequence(NULL, s, false, parent);
|
||||
|
||||
loaded_sequences.append(m);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Media* LoadThread::find_loaded_folder_by_id(int id) {
|
||||
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() {
|
||||
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
|
||||
int element_count = 0;
|
||||
while (!stream.atEnd()) {
|
||||
stream.readNextStartElement();
|
||||
if (stream.name() == "folder"
|
||||
|| stream.name() == "footage"
|
||||
|| stream.name() == "sequence"
|
||||
|| stream.name() == "clip"
|
||||
|| stream.name() == "effect") {
|
||||
element_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (parent > 0) {
|
||||
find_loaded_folder_by_id(parent)->appendChild(folder);
|
||||
} else {
|
||||
project_model.addTopLevelItem(folder);
|
||||
}
|
||||
}
|
||||
|
||||
cont = load_worker(file, stream, MEDIA_TYPE_FOOTAGE);
|
||||
}
|
||||
|
||||
// load sequences
|
||||
if (cont) {
|
||||
cont = load_worker(file, stream, MEDIA_TYPE_SEQUENCE);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (show_err) QMessageBox::critical(mainWindow, "Project Load Error", "Error loading project: " + error_str, QMessageBox::Ok);
|
||||
} else if (stream.hasError()) {
|
||||
dout << "[ERROR] Error parsing XML." << stream.errorString();
|
||||
QMessageBox::critical(mainWindow, "XML Parsing Error", "Couldn't load '" + project_url + "'. " + stream.errorString(), QMessageBox::Ok);
|
||||
cont = false;
|
||||
}
|
||||
|
||||
if (cont) {
|
||||
mainWindow->setWindowModified(autorecovery);
|
||||
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
panel_project->new_project();
|
||||
}
|
||||
|
||||
panel_project->add_recent_project(project_url);
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
void LoadThread::success_func() {
|
||||
if (open_seq != NULL) set_sequence(open_seq);
|
||||
update_ui(false);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef LOADTHREAD_H
|
||||
#define LOADTHREAD_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QDir>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
class Media;
|
||||
class Footage;
|
||||
class Clip;
|
||||
class Sequence;
|
||||
class LoadDialog;
|
||||
|
||||
class LoadThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LoadThread(LoadDialog* l, bool a);
|
||||
void run();
|
||||
signals:
|
||||
void success();
|
||||
private slots:
|
||||
void success_func();
|
||||
private:
|
||||
LoadDialog* ld;
|
||||
bool autorecovery;
|
||||
|
||||
bool load_worker(QFile& f, QXmlStreamReader& stream, int type);
|
||||
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;
|
||||
|
||||
QVector<Media*> loaded_folders;
|
||||
QVector<Clip*> loaded_clips;
|
||||
QVector<Media*> loaded_sequences;
|
||||
Media* find_loaded_folder_by_id(int id);
|
||||
};
|
||||
|
||||
#endif // LOADTHREAD_H
|
||||
+62
-54
@@ -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.toLatin1();
|
||||
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());
|
||||
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.toLatin1(), 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
|
||||
|
||||
+5
-8
@@ -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"
|
||||
@@ -222,9 +223,6 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {
|
||||
panel_sequence_viewer->viewer_widget->delete_function();
|
||||
panel_footage_viewer->viewer_widget->delete_function();
|
||||
|
||||
set_sequence(NULL);
|
||||
|
||||
QString data_dir = get_data_path();
|
||||
@@ -977,12 +975,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);
|
||||
|
||||
@@ -33,7 +33,6 @@ SOURCES += \
|
||||
ui/sourcetable.cpp \
|
||||
dialogs/aboutdialog.cpp \
|
||||
ui/timelinewidget.cpp \
|
||||
io/media.cpp \
|
||||
project/sequence.cpp \
|
||||
project/clip.cpp \
|
||||
playback/playback.cpp \
|
||||
@@ -91,7 +90,11 @@ SOURCES += \
|
||||
project/effectfield.cpp \
|
||||
effects/internal/cubetransition.cpp \
|
||||
project/effectgizmo.cpp \
|
||||
io/clipboard.cpp
|
||||
io/clipboard.cpp \
|
||||
io/loadthread.cpp \
|
||||
project/footage.cpp \
|
||||
project/media.cpp \
|
||||
project/projectmodel.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
@@ -102,7 +105,6 @@ HEADERS += \
|
||||
ui/sourcetable.h \
|
||||
dialogs/aboutdialog.h \
|
||||
ui/timelinewidget.h \
|
||||
io/media.h \
|
||||
project/sequence.h \
|
||||
project/clip.h \
|
||||
playback/playback.h \
|
||||
@@ -162,7 +164,11 @@ HEADERS += \
|
||||
project/effectfield.h \
|
||||
effects/internal/cubetransition.h \
|
||||
project/effectgizmo.h \
|
||||
io/clipboard.h
|
||||
io/clipboard.h \
|
||||
io/loadthread.h \
|
||||
project/footage.h \
|
||||
project/media.h \
|
||||
project/projectmodel.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui \
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "timeline.h"
|
||||
#include "effectcontrols.h"
|
||||
#include "viewer.h"
|
||||
#include "project.h"
|
||||
#include "project/sequence.h"
|
||||
#include "project/clip.h"
|
||||
#include "project/transition.h"
|
||||
@@ -100,6 +101,7 @@ void update_ui(bool modified) {
|
||||
if (modified) {
|
||||
update_effect_controls();
|
||||
}
|
||||
project_model.update_data();
|
||||
panel_effect_controls->update_keyframes();
|
||||
panel_timeline->repaint_timeline();
|
||||
panel_sequence_viewer->update_viewer();
|
||||
|
||||
+222
-961
File diff suppressed because it is too large
Load Diff
+37
-42
@@ -6,13 +6,15 @@
|
||||
#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;
|
||||
@@ -30,18 +32,20 @@ extern QString project_url;
|
||||
extern QStringList recent_projects;
|
||||
extern QString recent_proj_file;
|
||||
|
||||
int get_type_from_tree(QTreeWidgetItem* item);
|
||||
extern ProjectModel project_model;
|
||||
|
||||
/*int get_type_from_tree(Media *item);
|
||||
void* get_media_from_tree(QTreeWidgetItem* item);
|
||||
Media* get_footage_from_tree(QTreeWidgetItem* item);
|
||||
void set_footage_of_tree(QTreeWidgetItem* item, Media* media);
|
||||
Footage* get_footage_from_tree(QTreeWidgetItem* item);
|
||||
void set_footage_of_tree(QTreeWidgetItem* item, Footage* 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);
|
||||
void set_item_to_folder(QTreeWidgetItem* item);*/
|
||||
//void update_footage_tooltip(Media* item, Footage* media, QString error = 0);
|
||||
|
||||
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 +57,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(bool recursive, QStringList& files, Media *parent, Media* replace);
|
||||
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);
|
||||
|
||||
void save_recent_projects();
|
||||
|
||||
QVector<Sequence*> list_all_project_sequences();
|
||||
QVector<Media*> list_all_project_sequences();
|
||||
|
||||
SourceTable* source_table;
|
||||
|
||||
QVector<Media*> last_imported_media;
|
||||
QVector<Media*> last_imported_media;
|
||||
|
||||
//Media *new_item();
|
||||
|
||||
void start_preview_generator(Media* item, bool replacing);
|
||||
|
||||
Ui::Project *ui;
|
||||
public slots:
|
||||
void import_dialog();
|
||||
void delete_selected_media();
|
||||
@@ -83,48 +93,33 @@ 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 rename_media(Media* 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
|
||||
|
||||
+2
-38
@@ -34,55 +34,19 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="SourceTable" name="treeWidget">
|
||||
<widget class="SourceTable" name="treeView">
|
||||
<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>
|
||||
@@ -91,7 +55,7 @@
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>SourceTable</class>
|
||||
<extends>QTreeWidget</extends>
|
||||
<extends>QTreeView</extends>
|
||||
<header>ui/sourcetable.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
|
||||
+18
-18
@@ -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"
|
||||
|
||||
@@ -160,23 +161,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 +189,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 +205,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 +275,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 +300,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();
|
||||
|
||||
+5
-5
@@ -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);
|
||||
|
||||
+78
-76
@@ -8,7 +8,8 @@
|
||||
#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"
|
||||
@@ -33,6 +34,7 @@ Viewer::Viewer(QWidget *parent) :
|
||||
QDockWidget(parent),
|
||||
playing(false),
|
||||
just_played(false),
|
||||
media(NULL),
|
||||
seq(NULL),
|
||||
ui(new Ui::Viewer),
|
||||
created_sequence(false),
|
||||
@@ -46,7 +48,7 @@ Viewer::Viewer(QWidget *parent) :
|
||||
ui->glViewerPane->child = ui->openGLWidget;
|
||||
ui->openGLWidget->viewer = this;
|
||||
viewer_widget = ui->openGLWidget;
|
||||
set_media(MEDIA_TYPE_SEQUENCE, NULL);
|
||||
set_media(NULL);
|
||||
|
||||
ui->currentTimecode->setEnabled(false);
|
||||
ui->currentTimecode->set_minimum_value(0);
|
||||
@@ -344,23 +346,23 @@ void Viewer::pause() {
|
||||
|
||||
// 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);
|
||||
@@ -414,83 +416,83 @@ void Viewer::set_out_point() {
|
||||
ui->headers->set_out_point(seq->playhead);
|
||||
}
|
||||
|
||||
void Viewer::set_media(int type, void* media) {
|
||||
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() {
|
||||
|
||||
+3
-1
@@ -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);
|
||||
@@ -65,6 +66,7 @@ public:
|
||||
|
||||
ViewerWidget* viewer_widget;
|
||||
|
||||
Media* media;
|
||||
Sequence* seq;
|
||||
|
||||
Ui::Viewer *ui;
|
||||
|
||||
+393
-402
@@ -3,7 +3,7 @@
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "project/transition.h"
|
||||
#include "io/media.h"
|
||||
#include "project/footage.h"
|
||||
#include "playback/audio.h"
|
||||
#include "playback/playback.h"
|
||||
#include "project/effect.h"
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "playback/audio.h"
|
||||
#include "panels/panels.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "project/media.h"
|
||||
#include "debug.h"
|
||||
|
||||
extern "C" {
|
||||
@@ -49,7 +50,7 @@ void apply_audio_effects(Clip* c, double timecode_start, AVFrame* frame, int nb_
|
||||
if (e->is_enabled()) e->process_audio(timecode_start, timecode_end, frame->data[0], nb_bytes, 2);
|
||||
}
|
||||
if (c->get_opening_transition() != NULL) {
|
||||
if (c->media_type == MEDIA_TYPE_FOOTAGE) {
|
||||
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
double transition_start = (c->get_clip_in_with_transition() / c->sequence->frame_rate);
|
||||
double transition_end = (c->get_clip_in_with_transition() + c->get_opening_transition()->get_length()) / c->sequence->frame_rate;
|
||||
if (timecode_end < transition_end) {
|
||||
@@ -61,7 +62,7 @@ void apply_audio_effects(Clip* c, double timecode_start, AVFrame* frame, int nb_
|
||||
}
|
||||
}
|
||||
if (c->get_closing_transition() != NULL) {
|
||||
if (c->media_type == MEDIA_TYPE_FOOTAGE) {
|
||||
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
long length_with_transitions = c->get_timeline_out_with_transition() - c->get_timeline_in_with_transition();
|
||||
double transition_start = (c->get_clip_in_with_transition() + length_with_transitions - c->get_closing_transition()->get_length()) / c->sequence->frame_rate;
|
||||
double transition_end = (c->get_clip_in_with_transition() + length_with_transitions) / c->sequence->frame_rate;
|
||||
@@ -114,250 +115,246 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
|
||||
AVFrame* frame;
|
||||
int nb_bytes = INT_MAX;
|
||||
|
||||
switch (c->media_type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
double timebase = av_q2d(c->stream->time_base);
|
||||
if (c->media == NULL) {
|
||||
frame = c->frame;
|
||||
nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format)) * frame->channels;
|
||||
while ((c->frame_sample_index == -1 || c->frame_sample_index >= nb_bytes) && nb_bytes > 0) {
|
||||
// create "new frame"
|
||||
memset(c->frame->data[0], 0, nb_bytes);
|
||||
apply_audio_effects(c, bytes_to_seconds(frame->pts, frame->channels, frame->sample_rate), frame, nb_bytes, nests);
|
||||
c->frame->pts += nb_bytes;
|
||||
c->frame_sample_index = 0;
|
||||
if (c->audio_buffer_write == 0) {
|
||||
c->audio_buffer_write = get_buffer_offset_from_frame(last_fr, qMax(timeline_in, target_frame));
|
||||
}
|
||||
int offset = audio_ibuffer_read - c->audio_buffer_write;
|
||||
if (offset > 0) {
|
||||
c->audio_buffer_write += offset;
|
||||
c->frame_sample_index += offset;
|
||||
}
|
||||
}
|
||||
} else if (c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
double timebase = av_q2d(c->stream->time_base);
|
||||
|
||||
frame = c->queue.at(0);
|
||||
frame = c->queue.at(0);
|
||||
|
||||
// retrieve frame
|
||||
bool new_frame = false;
|
||||
while ((c->frame_sample_index == -1 || c->frame_sample_index >= nb_bytes) && nb_bytes > 0) {
|
||||
// no more audio left in frame, get a new one
|
||||
if (!c->reached_end) {
|
||||
int loop = 0;
|
||||
bool new_frame = false;
|
||||
while ((c->frame_sample_index == -1 || c->frame_sample_index >= nb_bytes) && nb_bytes > 0) {
|
||||
// no more audio left in frame, get a new one
|
||||
if (!c->reached_end) {
|
||||
int loop = 0;
|
||||
|
||||
if (c->reverse && !c->audio_just_reset) {
|
||||
avcodec_flush_buffers(c->codecCtx);
|
||||
c->reached_end = false;
|
||||
int64_t backtrack_seek = qMax(c->reverse_target - static_cast<int64_t>(av_q2d(av_inv_q(c->stream->time_base))), static_cast<int64_t>(0));
|
||||
av_seek_frame(c->formatCtx, c->stream->index, backtrack_seek, AVSEEK_FLAG_BACKWARD);
|
||||
if (c->reverse && !c->audio_just_reset) {
|
||||
avcodec_flush_buffers(c->codecCtx);
|
||||
c->reached_end = false;
|
||||
int64_t backtrack_seek = qMax(c->reverse_target - static_cast<int64_t>(av_q2d(av_inv_q(c->stream->time_base))), static_cast<int64_t>(0));
|
||||
av_seek_frame(c->formatCtx, c->stream->index, backtrack_seek, AVSEEK_FLAG_BACKWARD);
|
||||
#ifdef AUDIOWARNINGS
|
||||
if (backtrack_seek == 0) {
|
||||
dout << "backtracked to 0";
|
||||
}
|
||||
if (backtrack_seek == 0) {
|
||||
dout << "backtracked to 0";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
av_frame_unref(frame);
|
||||
do {
|
||||
av_frame_unref(frame);
|
||||
|
||||
int ret;
|
||||
int ret;
|
||||
|
||||
while ((ret = av_buffersink_get_frame(c->buffersink_ctx, frame)) == AVERROR(EAGAIN)) {
|
||||
ret = retrieve_next_frame(c, c->frame);
|
||||
if (ret >= 0) {
|
||||
if ((ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, c->frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
|
||||
dout << "[ERROR] Could not feed filtergraph -" << ret;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (ret == AVERROR_EOF) {
|
||||
while ((ret = av_buffersink_get_frame(c->buffersink_ctx, frame)) == AVERROR(EAGAIN)) {
|
||||
ret = retrieve_next_frame(c, c->frame);
|
||||
if (ret >= 0) {
|
||||
if ((ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, c->frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
|
||||
dout << "[ERROR] Could not feed filtergraph -" << ret;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (ret == AVERROR_EOF) {
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "reached EOF while reading";
|
||||
dout << "reached EOF while reading";
|
||||
#endif
|
||||
// TODO revise usage of reached_end in audio
|
||||
if (!c->reverse) {
|
||||
c->reached_end = true;
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
dout << "[WARNING] Raw audio frame data could not be retrieved." << ret;
|
||||
c->reached_end = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO revise usage of reached_end in audio
|
||||
if (!c->reverse) {
|
||||
c->reached_end = true;
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
dout << "[WARNING] Raw audio frame data could not be retrieved." << ret;
|
||||
c->reached_end = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret != AVERROR_EOF) {
|
||||
dout << "[ERROR] Could not pull from filtergraph";
|
||||
c->reached_end = true;
|
||||
break;
|
||||
} else {
|
||||
if (ret < 0) {
|
||||
if (ret != AVERROR_EOF) {
|
||||
dout << "[ERROR] Could not pull from filtergraph";
|
||||
c->reached_end = true;
|
||||
break;
|
||||
} else {
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "reached EOF while pulling from filtergraph";
|
||||
dout << "reached EOF while pulling from filtergraph";
|
||||
#endif
|
||||
if (!c->reverse) break;
|
||||
}
|
||||
}
|
||||
if (!c->reverse) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (c->reverse) {
|
||||
if (loop > 1) {
|
||||
AVFrame* rev_frame = c->queue.at(1);
|
||||
if (ret != AVERROR_EOF) {
|
||||
if (loop == 2) {
|
||||
if (c->reverse) {
|
||||
if (loop > 1) {
|
||||
AVFrame* rev_frame = c->queue.at(1);
|
||||
if (ret != AVERROR_EOF) {
|
||||
if (loop == 2) {
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "starting rev_frame";
|
||||
dout << "starting rev_frame";
|
||||
#endif
|
||||
rev_frame->nb_samples = 0;
|
||||
rev_frame->pts = c->frame->pkt_pts;
|
||||
}
|
||||
int offset = rev_frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(rev_frame->format)) * rev_frame->channels;
|
||||
rev_frame->nb_samples = 0;
|
||||
rev_frame->pts = c->frame->pkt_pts;
|
||||
}
|
||||
int offset = rev_frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(rev_frame->format)) * rev_frame->channels;
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "offset 1:" << offset;
|
||||
dout << "retrieved samples:" << frame->nb_samples << "size:" << (frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format)) * frame->channels);
|
||||
dout << "offset 1:" << offset;
|
||||
dout << "retrieved samples:" << frame->nb_samples << "size:" << (frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format)) * frame->channels);
|
||||
#endif
|
||||
memcpy(
|
||||
rev_frame->data[0]+offset,
|
||||
frame->data[0],
|
||||
(frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format)) * frame->channels)
|
||||
);
|
||||
memcpy(
|
||||
rev_frame->data[0]+offset,
|
||||
frame->data[0],
|
||||
(frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format)) * frame->channels)
|
||||
);
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "pts:" << c->frame->pts << "dur:" << c->frame->pkt_duration << "rev_target:" << c->reverse_target << "offset:" << offset << "limit:" << rev_frame->linesize[0];
|
||||
dout << "pts:" << c->frame->pts << "dur:" << c->frame->pkt_duration << "rev_target:" << c->reverse_target << "offset:" << offset << "limit:" << rev_frame->linesize[0];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
rev_frame->nb_samples += frame->nb_samples;
|
||||
rev_frame->nb_samples += frame->nb_samples;
|
||||
|
||||
if ((c->frame->pts >= c->reverse_target) || (ret == AVERROR_EOF)) {
|
||||
if ((c->frame->pts >= c->reverse_target) || (ret == AVERROR_EOF)) {
|
||||
/*
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "time for the end of rev cache" << rev_frame->nb_samples << c->rev_target << c->frame->pts << c->frame->pkt_duration << c->frame->nb_samples;
|
||||
dout << "diff:" << (c->frame->pkt_pts + c->frame->pkt_duration) - c->rev_target;
|
||||
dout << "time for the end of rev cache" << rev_frame->nb_samples << c->rev_target << c->frame->pts << c->frame->pkt_duration << c->frame->nb_samples;
|
||||
dout << "diff:" << (c->frame->pkt_pts + c->frame->pkt_duration) - c->rev_target;
|
||||
#endif
|
||||
int cutoff = qRound64((((c->frame->pkt_pts + c->frame->pkt_duration) - c->reverse_target) * timebase) * audio_output->format().sampleRate());
|
||||
if (cutoff > 0) {
|
||||
if (cutoff > 0) {
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "cut off" << cutoff << "samples (rate:" << audio_output->format().sampleRate() << ")";
|
||||
#endif
|
||||
rev_frame->nb_samples -= cutoff;
|
||||
}
|
||||
rev_frame->nb_samples -= cutoff;
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "pre cutoff deets::: rev_frame.pts:" << rev_frame->pts << "rev_frame.nb_samples" << rev_frame->nb_samples << "rev_target:" << c->reverse_target;
|
||||
dout << "pre cutoff deets::: rev_frame.pts:" << rev_frame->pts << "rev_frame.nb_samples" << rev_frame->nb_samples << "rev_target:" << c->reverse_target;
|
||||
#endif
|
||||
rev_frame->nb_samples = qRound64(static_cast<double>(c->reverse_target - rev_frame->pts) / c->stream->codecpar->sample_rate * (audio_output->format().sampleRate() / c->speed));
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "post cutoff deets::" << rev_frame->nb_samples;
|
||||
dout << "post cutoff deets::" << rev_frame->nb_samples;
|
||||
#endif
|
||||
|
||||
int frame_size = rev_frame->nb_samples * rev_frame->channels * av_get_bytes_per_sample(static_cast<AVSampleFormat>(rev_frame->format));
|
||||
int half_frame_size = frame_size >> 1;
|
||||
int frame_size = rev_frame->nb_samples * rev_frame->channels * av_get_bytes_per_sample(static_cast<AVSampleFormat>(rev_frame->format));
|
||||
int half_frame_size = frame_size >> 1;
|
||||
|
||||
int sample_size = rev_frame->channels*av_get_bytes_per_sample(static_cast<AVSampleFormat>(rev_frame->format));
|
||||
char* temp_chars = new char[sample_size];
|
||||
for (int i=0;i<half_frame_size;i+=sample_size) {
|
||||
for (int j=0;j<sample_size;j++) {
|
||||
temp_chars[j] = rev_frame->data[0][i+j];
|
||||
}
|
||||
for (int j=0;j<sample_size;j++) {
|
||||
rev_frame->data[0][i+j] = rev_frame->data[0][frame_size-i-sample_size+j];
|
||||
}
|
||||
for (int j=0;j<sample_size;j++) {
|
||||
rev_frame->data[0][frame_size-i-sample_size+j] = temp_chars[j];
|
||||
}
|
||||
}
|
||||
delete [] temp_chars;
|
||||
int sample_size = rev_frame->channels*av_get_bytes_per_sample(static_cast<AVSampleFormat>(rev_frame->format));
|
||||
char* temp_chars = new char[sample_size];
|
||||
for (int i=0;i<half_frame_size;i+=sample_size) {
|
||||
for (int j=0;j<sample_size;j++) {
|
||||
temp_chars[j] = rev_frame->data[0][i+j];
|
||||
}
|
||||
for (int j=0;j<sample_size;j++) {
|
||||
rev_frame->data[0][i+j] = rev_frame->data[0][frame_size-i-sample_size+j];
|
||||
}
|
||||
for (int j=0;j<sample_size;j++) {
|
||||
rev_frame->data[0][frame_size-i-sample_size+j] = temp_chars[j];
|
||||
}
|
||||
}
|
||||
delete [] temp_chars;
|
||||
|
||||
c->reverse_target = rev_frame->pts;
|
||||
frame = rev_frame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
c->reverse_target = rev_frame->pts;
|
||||
frame = rev_frame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
loop++;
|
||||
loop++;
|
||||
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "loop" << loop;
|
||||
dout << "loop" << loop;
|
||||
#endif
|
||||
} else {
|
||||
frame->pts = c->frame->pts;
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
} else {
|
||||
// if there is no more data in the file, we flush the remainder out of swresample
|
||||
break;
|
||||
} else {
|
||||
frame->pts = c->frame->pts;
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
} else {
|
||||
// if there is no more data in the file, we flush the remainder out of swresample
|
||||
break;
|
||||
}
|
||||
|
||||
new_frame = true;
|
||||
new_frame = true;
|
||||
|
||||
if (c->frame_sample_index < 0) {
|
||||
c->frame_sample_index = 0;
|
||||
} else {
|
||||
c->frame_sample_index -= nb_bytes;
|
||||
}
|
||||
if (c->frame_sample_index < 0) {
|
||||
c->frame_sample_index = 0;
|
||||
} else {
|
||||
c->frame_sample_index -= nb_bytes;
|
||||
}
|
||||
|
||||
nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format)) * frame->channels;
|
||||
nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format)) * frame->channels;
|
||||
|
||||
if (c->audio_just_reset) {
|
||||
// get precise sample offset for the elected clip_in from this audio frame
|
||||
double target_sts = playhead_to_clip_seconds(c, c->audio_target_frame);
|
||||
double frame_sts = ((frame->pts - c->stream->start_time) * timebase);
|
||||
double target_sts = playhead_to_clip_seconds(c, c->audio_target_frame);
|
||||
double frame_sts = ((frame->pts - c->stream->start_time) * timebase);
|
||||
int nb_samples = qRound64((target_sts - frame_sts)*audio_output->format().sampleRate());
|
||||
c->frame_sample_index = nb_samples * 4;
|
||||
c->frame_sample_index = nb_samples * 4;
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "fsts:" << frame_sts << "tsts:" << target_sts << "nbs:" << nb_samples << "nbb:" << nb_bytes << "rev_targetToSec:" << (c->reverse_target * timebase);
|
||||
dout << "fsi-calc:" << c->frame_sample_index;
|
||||
dout << "fsts:" << frame_sts << "tsts:" << target_sts << "nbs:" << nb_samples << "nbb:" << nb_bytes << "rev_targetToSec:" << (c->reverse_target * timebase);
|
||||
dout << "fsi-calc:" << c->frame_sample_index;
|
||||
#endif
|
||||
if (c->reverse) c->frame_sample_index = nb_bytes - c->frame_sample_index;
|
||||
if (c->reverse) c->frame_sample_index = nb_bytes - c->frame_sample_index;
|
||||
c->audio_just_reset = false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "fsi-post-post:" << c->frame_sample_index;
|
||||
dout << "fsi-post-post:" << c->frame_sample_index;
|
||||
#endif
|
||||
if (c->audio_buffer_write == 0) {
|
||||
c->audio_buffer_write = get_buffer_offset_from_frame(last_fr, qMax(timeline_in, target_frame));
|
||||
if (c->audio_buffer_write == 0) {
|
||||
c->audio_buffer_write = get_buffer_offset_from_frame(last_fr, qMax(timeline_in, target_frame));
|
||||
|
||||
if (frame_skip > 0) {
|
||||
int target = get_buffer_offset_from_frame(last_fr, qMax(timeline_in + frame_skip, target_frame));
|
||||
c->frame_sample_index += (target - c->audio_buffer_write);
|
||||
c->audio_buffer_write = target;
|
||||
}
|
||||
}
|
||||
if (frame_skip > 0) {
|
||||
int target = get_buffer_offset_from_frame(last_fr, qMax(timeline_in + frame_skip, target_frame));
|
||||
c->frame_sample_index += (target - c->audio_buffer_write);
|
||||
c->audio_buffer_write = target;
|
||||
}
|
||||
}
|
||||
|
||||
int offset = audio_ibuffer_read - c->audio_buffer_write;
|
||||
if (offset > 0) {
|
||||
c->audio_buffer_write += offset;
|
||||
c->frame_sample_index += offset;
|
||||
}
|
||||
int offset = audio_ibuffer_read - c->audio_buffer_write;
|
||||
if (offset > 0) {
|
||||
c->audio_buffer_write += offset;
|
||||
c->frame_sample_index += offset;
|
||||
}
|
||||
|
||||
// try to correct negative fsi
|
||||
if (c->frame_sample_index < 0) {
|
||||
c->audio_buffer_write -= c->frame_sample_index;
|
||||
c->frame_sample_index = 0;
|
||||
}
|
||||
}
|
||||
// try to correct negative fsi
|
||||
if (c->frame_sample_index < 0) {
|
||||
c->audio_buffer_write -= c->frame_sample_index;
|
||||
c->frame_sample_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (c->reverse) frame = c->queue.at(1);
|
||||
if (c->reverse) frame = c->queue.at(1);
|
||||
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "j" << c->frame_sample_index << nb_bytes;
|
||||
dout << "j" << c->frame_sample_index << nb_bytes;
|
||||
#endif
|
||||
|
||||
// apply any audio effects to the data
|
||||
if (nb_bytes == INT_MAX) nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format)) * frame->channels;
|
||||
if (new_frame) {
|
||||
// apply any audio effects to the data
|
||||
if (nb_bytes == INT_MAX) nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format)) * frame->channels;
|
||||
if (new_frame) {
|
||||
apply_audio_effects(c, bytes_to_seconds(c->audio_buffer_write, 2, audio_output->format().sampleRate()) + audio_ibuffer_timecode + ((double)c->get_clip_in_with_transition()/c->sequence->frame_rate) - ((double)timeline_in/last_fr), frame, nb_bytes, nests);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_TONE:
|
||||
frame = c->frame;
|
||||
nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format)) * frame->channels;
|
||||
while ((c->frame_sample_index == -1 || c->frame_sample_index >= nb_bytes) && nb_bytes > 0) {
|
||||
// create "new frame"
|
||||
memset(c->frame->data[0], 0, nb_bytes);
|
||||
apply_audio_effects(c, bytes_to_seconds(frame->pts, frame->channels, frame->sample_rate), frame, nb_bytes, nests);
|
||||
c->frame->pts += nb_bytes;
|
||||
c->frame_sample_index = 0;
|
||||
if (c->audio_buffer_write == 0) {
|
||||
c->audio_buffer_write = get_buffer_offset_from_frame(last_fr, qMax(timeline_in, target_frame));
|
||||
}
|
||||
int offset = audio_ibuffer_read - c->audio_buffer_write;
|
||||
if (offset > 0) {
|
||||
c->audio_buffer_write += offset;
|
||||
c->frame_sample_index += offset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: // shouldn't ever get here
|
||||
dout << "[ERROR] Tried to cache a non-footage/tone clip";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// shouldn't ever get here
|
||||
dout << "[ERROR] Tried to cache a non-footage/tone clip";
|
||||
return;
|
||||
}
|
||||
|
||||
// mix audio into internal buffer
|
||||
@@ -452,8 +449,8 @@ void cache_video_worker(Clip* c, long playhead) {
|
||||
while (true) {
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
|
||||
Media* media = static_cast<Media*>(c->media);
|
||||
MediaStream* ms = media->get_stream_from_file_index(true, c->media_stream);
|
||||
Footage* media = c->media->to_footage();
|
||||
FootageStream* ms = media->get_stream_from_file_index(true, c->media_stream);
|
||||
|
||||
while ((retr_ret = av_buffersink_get_frame(c->buffersink_ctx, frame)) == AVERROR(EAGAIN)) {
|
||||
if (c->multithreaded && c->cacher->interrupt) return; // abort
|
||||
@@ -539,85 +536,83 @@ void cache_video_worker(Clip* c, long playhead) {
|
||||
|
||||
void reset_cache(Clip* c, long target_frame) {
|
||||
// if we seek to a whole other place in the timeline, we'll need to reset the cache with new values
|
||||
switch (c->media_type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
MediaStream* ms = static_cast<Media*>(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
if (ms->infinite_length) {
|
||||
/*avcodec_flush_buffers(c->codecCtx);
|
||||
av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD);*/
|
||||
c->use_existing_frame = false;
|
||||
} else {
|
||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
// clear current queue
|
||||
c->queue_clear();
|
||||
if (c->media == NULL) {
|
||||
if (c->track >= 0) {
|
||||
// tone clip
|
||||
c->reached_end = false;
|
||||
c->audio_target_frame = target_frame;
|
||||
c->frame_sample_index = -1;
|
||||
c->frame->pts = 0;
|
||||
}
|
||||
} else {
|
||||
FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
if (ms->infinite_length) {
|
||||
/*avcodec_flush_buffers(c->codecCtx);
|
||||
av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD);*/
|
||||
c->use_existing_frame = false;
|
||||
} else {
|
||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
// clear current queue
|
||||
c->queue_clear();
|
||||
|
||||
// seeks to nearest keyframe (target_frame represents internal clip frame)
|
||||
// seeks to nearest keyframe (target_frame represents internal clip frame)
|
||||
int64_t target_ts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame));
|
||||
int64_t seek_ts = target_ts;
|
||||
int64_t timebase_half_second = qRound64(av_q2d(av_inv_q(c->stream->time_base)));
|
||||
int64_t seek_ts = target_ts;
|
||||
int64_t timebase_half_second = qRound64(av_q2d(av_inv_q(c->stream->time_base)));
|
||||
if (c->reverse) seek_ts -= timebase_half_second;
|
||||
|
||||
while (true) {
|
||||
// flush ffmpeg codecs
|
||||
avcodec_flush_buffers(c->codecCtx);
|
||||
c->reached_end = false;
|
||||
while (true) {
|
||||
// flush ffmpeg codecs
|
||||
avcodec_flush_buffers(c->codecCtx);
|
||||
c->reached_end = false;
|
||||
|
||||
if (seek_ts > 0) {
|
||||
av_seek_frame(c->formatCtx, ms->file_index, seek_ts, AVSEEK_FLAG_BACKWARD);
|
||||
if (seek_ts > 0) {
|
||||
av_seek_frame(c->formatCtx, ms->file_index, seek_ts, AVSEEK_FLAG_BACKWARD);
|
||||
|
||||
av_frame_unref(c->frame);
|
||||
int ret = retrieve_next_frame(c, c->frame);
|
||||
if (ret < 0) {
|
||||
dout << "[WARNING] Seeking terminated prematurely";
|
||||
break;
|
||||
av_frame_unref(c->frame);
|
||||
int ret = retrieve_next_frame(c, c->frame);
|
||||
if (ret < 0) {
|
||||
dout << "[WARNING] Seeking terminated prematurely";
|
||||
break;
|
||||
}
|
||||
if (c->frame->pts <= target_ts) {
|
||||
c->use_existing_frame = true;
|
||||
break;
|
||||
} else {
|
||||
seek_ts -= timebase_half_second;
|
||||
}
|
||||
} else {
|
||||
av_frame_unref(c->frame);
|
||||
av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD);
|
||||
c->use_existing_frame = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
// flush ffmpeg codecs
|
||||
avcodec_flush_buffers(c->codecCtx);
|
||||
c->reached_end = false;
|
||||
if (c->frame->pts <= target_ts) {
|
||||
c->use_existing_frame = true;
|
||||
break;
|
||||
} else {
|
||||
seek_ts -= timebase_half_second;
|
||||
}
|
||||
} else {
|
||||
av_frame_unref(c->frame);
|
||||
av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD);
|
||||
c->use_existing_frame = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
// flush ffmpeg codecs
|
||||
avcodec_flush_buffers(c->codecCtx);
|
||||
c->reached_end = false;
|
||||
|
||||
// seek (target_frame represents timeline timecode in frames, not clip timecode)
|
||||
// seek (target_frame represents timeline timecode in frames, not clip timecode)
|
||||
|
||||
int64_t timestamp = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame));
|
||||
int64_t timestamp = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame));
|
||||
|
||||
if (c->reverse) {
|
||||
c->reverse_target = timestamp;
|
||||
timestamp -= av_q2d(av_inv_q(c->stream->time_base));
|
||||
c->reverse_target = timestamp;
|
||||
timestamp -= av_q2d(av_inv_q(c->stream->time_base));
|
||||
#ifdef AUDIOWARNINGS
|
||||
dout << "seeking to" << timestamp << "(originally" << c->reverse_target << ")";
|
||||
} else {
|
||||
dout << "reset called; seeking to" << timestamp;
|
||||
dout << "seeking to" << timestamp << "(originally" << c->reverse_target << ")";
|
||||
} else {
|
||||
dout << "reset called; seeking to" << timestamp;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
av_seek_frame(c->formatCtx, ms->file_index, timestamp, AVSEEK_FLAG_BACKWARD);
|
||||
c->audio_target_frame = target_frame;
|
||||
c->frame_sample_index = -1;
|
||||
c->audio_just_reset = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_TONE:
|
||||
c->reached_end = false;
|
||||
c->audio_target_frame = target_frame;
|
||||
c->frame_sample_index = -1;
|
||||
c->frame->pts = 0;
|
||||
break;
|
||||
}
|
||||
c->audio_target_frame = target_frame;
|
||||
c->frame_sample_index = -1;
|
||||
c->audio_just_reset = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cacher::Cacher(Clip* c) : clip(c) {}
|
||||
@@ -625,85 +620,97 @@ Cacher::Cacher(Clip* c) : clip(c) {}
|
||||
AVSampleFormat sample_format = AV_SAMPLE_FMT_S16;
|
||||
|
||||
void open_clip_worker(Clip* clip) {
|
||||
switch (clip->media_type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
// opens file resource for FFmpeg and prepares Clip struct for playback
|
||||
Media* m = static_cast<Media*>(clip->media);
|
||||
QByteArray ba = m->url.toUtf8();
|
||||
const char* filename = ba.constData();
|
||||
MediaStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream);
|
||||
if (clip->media == NULL) {
|
||||
if (clip->track >= 0) {
|
||||
clip->frame = av_frame_alloc();
|
||||
clip->frame->format = sample_format;
|
||||
clip->frame->channel_layout = clip->sequence->audio_layout;
|
||||
clip->frame->channels = av_get_channel_layout_nb_channels(clip->frame->channel_layout);
|
||||
clip->frame->sample_rate = audio_output->format().sampleRate();
|
||||
clip->frame->nb_samples = 2048;
|
||||
av_frame_make_writable(clip->frame);
|
||||
if (av_frame_get_buffer(clip->frame, 0)) {
|
||||
dout << "[ERROR] Could not allocate buffer for tone clip";
|
||||
}
|
||||
clip->audio_reset = true;
|
||||
}
|
||||
} else if (clip->media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
// opens file resource for FFmpeg and prepares Clip struct for playback
|
||||
Footage* m = clip->media->to_footage();
|
||||
QByteArray ba = m->url.toUtf8();
|
||||
const char* filename = ba.constData();
|
||||
FootageStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream);
|
||||
|
||||
int errCode = avformat_open_input(
|
||||
&clip->formatCtx,
|
||||
filename,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
if (errCode != 0) {
|
||||
char err[1024];
|
||||
av_strerror(errCode, err, 1024);
|
||||
dout << "[ERROR] Could not open" << filename << "-" << err;
|
||||
return;
|
||||
}
|
||||
int errCode = avformat_open_input(
|
||||
&clip->formatCtx,
|
||||
filename,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
if (errCode != 0) {
|
||||
char err[1024];
|
||||
av_strerror(errCode, err, 1024);
|
||||
dout << "[ERROR] Could not open" << filename << "-" << err;
|
||||
return;
|
||||
}
|
||||
|
||||
errCode = avformat_find_stream_info(clip->formatCtx, NULL);
|
||||
if (errCode < 0) {
|
||||
char err[1024];
|
||||
av_strerror(errCode, err, 1024);
|
||||
dout << "[ERROR] Could not open" << filename << "-" << err;
|
||||
return;
|
||||
}
|
||||
errCode = avformat_find_stream_info(clip->formatCtx, NULL);
|
||||
if (errCode < 0) {
|
||||
char err[1024];
|
||||
av_strerror(errCode, err, 1024);
|
||||
dout << "[ERROR] Could not open" << filename << "-" << err;
|
||||
return;
|
||||
}
|
||||
|
||||
av_dump_format(clip->formatCtx, 0, filename, 0);
|
||||
av_dump_format(clip->formatCtx, 0, filename, 0);
|
||||
|
||||
clip->stream = clip->formatCtx->streams[ms->file_index];
|
||||
clip->codec = avcodec_find_decoder(clip->stream->codecpar->codec_id);
|
||||
clip->codecCtx = avcodec_alloc_context3(clip->codec);
|
||||
avcodec_parameters_to_context(clip->codecCtx, clip->stream->codecpar);
|
||||
clip->stream = clip->formatCtx->streams[ms->file_index];
|
||||
clip->codec = avcodec_find_decoder(clip->stream->codecpar->codec_id);
|
||||
clip->codecCtx = avcodec_alloc_context3(clip->codec);
|
||||
avcodec_parameters_to_context(clip->codecCtx, clip->stream->codecpar);
|
||||
|
||||
clip->max_queue_size = (ms->infinite_length) ? 1 : qCeil(ms->video_frame_rate*0.5);
|
||||
if (ms->video_interlacing != VIDEO_PROGRESSIVE) clip->max_queue_size *= 2;
|
||||
clip->max_queue_size = (ms->infinite_length) ? 1 : qCeil(ms->video_frame_rate*0.5);
|
||||
if (ms->video_interlacing != VIDEO_PROGRESSIVE) clip->max_queue_size *= 2;
|
||||
|
||||
clip->opts = NULL;
|
||||
|
||||
// optimized decoding settings
|
||||
// optimized decoding settings
|
||||
if (clip->stream->codecpar->codec_id != AV_CODEC_ID_PNG &&
|
||||
clip->stream->codecpar->codec_id != AV_CODEC_ID_APNG &&
|
||||
clip->stream->codecpar->codec_id != AV_CODEC_ID_TIFF &&
|
||||
clip->stream->codecpar->codec_id != AV_CODEC_ID_PSD) {
|
||||
clip->stream->codecpar->codec_id != AV_CODEC_ID_APNG &&
|
||||
clip->stream->codecpar->codec_id != AV_CODEC_ID_TIFF &&
|
||||
clip->stream->codecpar->codec_id != AV_CODEC_ID_PSD) {
|
||||
av_dict_set(&clip->opts, "threads", "auto", 0);
|
||||
}
|
||||
if (clip->stream->codecpar->codec_id == AV_CODEC_ID_H264) {
|
||||
}
|
||||
if (clip->stream->codecpar->codec_id == AV_CODEC_ID_H264) {
|
||||
av_dict_set(&clip->opts, "tune", "fastdecode", 0);
|
||||
av_dict_set(&clip->opts, "tune", "zerolatency", 0);
|
||||
}
|
||||
|
||||
// Open codec
|
||||
// Open codec
|
||||
if (avcodec_open2(clip->codecCtx, clip->codec, &clip->opts) < 0) {
|
||||
dout << "[ERROR] Could not open codec";
|
||||
}
|
||||
dout << "[ERROR] Could not open codec";
|
||||
}
|
||||
|
||||
// allocate filtergraph
|
||||
clip->filter_graph = avfilter_graph_alloc();
|
||||
if (clip->filter_graph == NULL) {
|
||||
dout << "[ERROR] Could not create filtergraph";
|
||||
}
|
||||
char filter_args[512];
|
||||
// allocate filtergraph
|
||||
clip->filter_graph = avfilter_graph_alloc();
|
||||
if (clip->filter_graph == NULL) {
|
||||
dout << "[ERROR] Could not create filtergraph";
|
||||
}
|
||||
char filter_args[512];
|
||||
|
||||
if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
snprintf(filter_args, sizeof(filter_args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
|
||||
clip->stream->codecpar->width,
|
||||
clip->stream->codecpar->height,
|
||||
clip->stream->codecpar->format,
|
||||
clip->stream->time_base.num,
|
||||
clip->stream->time_base.den,
|
||||
clip->stream->codecpar->sample_aspect_ratio.num,
|
||||
clip->stream->codecpar->sample_aspect_ratio.den
|
||||
);
|
||||
snprintf(filter_args, sizeof(filter_args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
|
||||
clip->stream->codecpar->width,
|
||||
clip->stream->codecpar->height,
|
||||
clip->stream->codecpar->format,
|
||||
clip->stream->time_base.num,
|
||||
clip->stream->time_base.den,
|
||||
clip->stream->codecpar->sample_aspect_ratio.num,
|
||||
clip->stream->codecpar->sample_aspect_ratio.den
|
||||
);
|
||||
|
||||
avfilter_graph_create_filter(&clip->buffersrc_ctx, avfilter_get_by_name("buffer"), "in", filter_args, NULL, clip->filter_graph);
|
||||
avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("buffersink"), "out", NULL, NULL, clip->filter_graph);
|
||||
avfilter_graph_create_filter(&clip->buffersrc_ctx, avfilter_get_by_name("buffer"), "in", filter_args, NULL, clip->filter_graph);
|
||||
avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("buffersink"), "out", NULL, NULL, clip->filter_graph);
|
||||
|
||||
/*enum AVPixelFormat sinkpix_fmts[] = { static_cast<AVPixelFormat>(dest_format), AV_PIX_FMT_NONE };
|
||||
if (av_opt_set_int_list(clip->buffersink_ctx, "pix_fmts", sinkpix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN) < 0) {
|
||||
@@ -713,14 +720,14 @@ void open_clip_worker(Clip* clip) {
|
||||
AVFilterContext* last_filter = clip->buffersrc_ctx;
|
||||
|
||||
if (ms->video_interlacing != VIDEO_PROGRESSIVE) {
|
||||
AVFilterContext* yadif_filter;
|
||||
char yadif_args[100];
|
||||
AVFilterContext* yadif_filter;
|
||||
char yadif_args[100];
|
||||
snprintf(yadif_args, sizeof(yadif_args), "mode=3:parity=%d", ((ms->video_interlacing == VIDEO_TOP_FIELD_FIRST) ? 0 : 1)); // there's a CUDA version if we start using nvdec/nvenc
|
||||
avfilter_graph_create_filter(&yadif_filter, avfilter_get_by_name("yadif"), "yadif", yadif_args, NULL, clip->filter_graph);
|
||||
avfilter_graph_create_filter(&yadif_filter, avfilter_get_by_name("yadif"), "yadif", yadif_args, NULL, clip->filter_graph);
|
||||
|
||||
avfilter_link(last_filter, 0, yadif_filter, 0);
|
||||
last_filter = yadif_filter;
|
||||
}
|
||||
}
|
||||
|
||||
/* stabilization code one day */
|
||||
bool stabilize = false;
|
||||
@@ -743,110 +750,95 @@ void open_clip_worker(Clip* clip) {
|
||||
avfilter_link(format_conv, 0, clip->buffersink_ctx, 0);
|
||||
|
||||
avfilter_graph_config(clip->filter_graph, NULL);
|
||||
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
if (clip->codecCtx->channel_layout == 0) clip->codecCtx->channel_layout = av_get_default_channel_layout(clip->stream->codecpar->channels);
|
||||
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
if (clip->codecCtx->channel_layout == 0) clip->codecCtx->channel_layout = av_get_default_channel_layout(clip->stream->codecpar->channels);
|
||||
|
||||
// set up cache
|
||||
clip->queue.append(av_frame_alloc());
|
||||
if (clip->reverse) {
|
||||
AVFrame* reverse_frame = av_frame_alloc();
|
||||
// set up cache
|
||||
clip->queue.append(av_frame_alloc());
|
||||
if (clip->reverse) {
|
||||
AVFrame* reverse_frame = av_frame_alloc();
|
||||
|
||||
reverse_frame->format = sample_format;
|
||||
reverse_frame->format = sample_format;
|
||||
reverse_frame->nb_samples = audio_output->format().sampleRate()*2;
|
||||
reverse_frame->channel_layout = clip->sequence->audio_layout;
|
||||
reverse_frame->channels = av_get_channel_layout_nb_channels(clip->sequence->audio_layout);
|
||||
av_frame_get_buffer(reverse_frame, 0);
|
||||
reverse_frame->channel_layout = clip->sequence->audio_layout;
|
||||
reverse_frame->channels = av_get_channel_layout_nb_channels(clip->sequence->audio_layout);
|
||||
av_frame_get_buffer(reverse_frame, 0);
|
||||
|
||||
clip->queue.append(reverse_frame);
|
||||
}
|
||||
clip->queue.append(reverse_frame);
|
||||
}
|
||||
|
||||
snprintf(filter_args, sizeof(filter_args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64,
|
||||
clip->stream->time_base.num,
|
||||
clip->stream->time_base.den,
|
||||
clip->stream->codecpar->sample_rate,
|
||||
av_get_sample_fmt_name(clip->codecCtx->sample_fmt),
|
||||
clip->codecCtx->channel_layout
|
||||
);
|
||||
snprintf(filter_args, sizeof(filter_args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64,
|
||||
clip->stream->time_base.num,
|
||||
clip->stream->time_base.den,
|
||||
clip->stream->codecpar->sample_rate,
|
||||
av_get_sample_fmt_name(clip->codecCtx->sample_fmt),
|
||||
clip->codecCtx->channel_layout
|
||||
);
|
||||
|
||||
avfilter_graph_create_filter(&clip->buffersrc_ctx, avfilter_get_by_name("abuffer"), "in", filter_args, NULL, clip->filter_graph);
|
||||
avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("abuffersink"), "out", NULL, NULL, clip->filter_graph);
|
||||
avfilter_graph_create_filter(&clip->buffersrc_ctx, avfilter_get_by_name("abuffer"), "in", filter_args, NULL, clip->filter_graph);
|
||||
avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("abuffersink"), "out", NULL, NULL, clip->filter_graph);
|
||||
|
||||
enum AVSampleFormat sample_fmts[] = { sample_format, static_cast<AVSampleFormat>(-1) };
|
||||
if (av_opt_set_int_list(clip->buffersink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN) < 0) {
|
||||
dout << "[ERROR] Could not set output sample format";
|
||||
}
|
||||
enum AVSampleFormat sample_fmts[] = { sample_format, static_cast<AVSampleFormat>(-1) };
|
||||
if (av_opt_set_int_list(clip->buffersink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN) < 0) {
|
||||
dout << "[ERROR] Could not set output sample format";
|
||||
}
|
||||
|
||||
int64_t channel_layouts[] = { AV_CH_LAYOUT_STEREO, static_cast<AVSampleFormat>(-1) };
|
||||
if (av_opt_set_int_list(clip->buffersink_ctx, "channel_layouts", channel_layouts, -1, AV_OPT_SEARCH_CHILDREN) < 0) {
|
||||
dout << "[ERROR] Could not set output sample format";
|
||||
}
|
||||
int64_t channel_layouts[] = { AV_CH_LAYOUT_STEREO, static_cast<AVSampleFormat>(-1) };
|
||||
if (av_opt_set_int_list(clip->buffersink_ctx, "channel_layouts", channel_layouts, -1, AV_OPT_SEARCH_CHILDREN) < 0) {
|
||||
dout << "[ERROR] Could not set output sample format";
|
||||
}
|
||||
|
||||
int target_sample_rate = audio_output->format().sampleRate();
|
||||
int target_sample_rate = audio_output->format().sampleRate();
|
||||
|
||||
if (qFuzzyCompare(clip->speed, 1.0)) {
|
||||
avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0);
|
||||
} else if (clip->maintain_audio_pitch) {
|
||||
AVFilterContext* previous_filter = clip->buffersrc_ctx;
|
||||
AVFilterContext* last_filter = clip->buffersrc_ctx;
|
||||
if (qFuzzyCompare(clip->speed, 1.0)) {
|
||||
avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0);
|
||||
} else if (clip->maintain_audio_pitch) {
|
||||
AVFilterContext* previous_filter = clip->buffersrc_ctx;
|
||||
AVFilterContext* last_filter = clip->buffersrc_ctx;
|
||||
|
||||
char speed_param[10];
|
||||
char speed_param[10];
|
||||
|
||||
if (clip->speed != 1.0) {
|
||||
double base = (clip->speed > 1.0) ? 2.0 : 0.5;
|
||||
if (clip->speed != 1.0) {
|
||||
double base = (clip->speed > 1.0) ? 2.0 : 0.5;
|
||||
|
||||
double speedlog = log(clip->speed) / log(base);
|
||||
int whole2 = qFloor(speedlog);
|
||||
speedlog -= whole2;
|
||||
double speedlog = log(clip->speed) / log(base);
|
||||
int whole2 = qFloor(speedlog);
|
||||
speedlog -= whole2;
|
||||
|
||||
if (whole2 > 0) {
|
||||
snprintf(speed_param, sizeof(speed_param), "%f", base);
|
||||
for (int i=0;i<whole2;i++) {
|
||||
AVFilterContext* tempo_filter = NULL;
|
||||
avfilter_graph_create_filter(&tempo_filter, avfilter_get_by_name("atempo"), "atempo", speed_param, NULL, clip->filter_graph);
|
||||
avfilter_link(previous_filter, 0, tempo_filter, 0);
|
||||
previous_filter = tempo_filter;
|
||||
}
|
||||
}
|
||||
if (whole2 > 0) {
|
||||
snprintf(speed_param, sizeof(speed_param), "%f", base);
|
||||
for (int i=0;i<whole2;i++) {
|
||||
AVFilterContext* tempo_filter = NULL;
|
||||
avfilter_graph_create_filter(&tempo_filter, avfilter_get_by_name("atempo"), "atempo", speed_param, NULL, clip->filter_graph);
|
||||
avfilter_link(previous_filter, 0, tempo_filter, 0);
|
||||
previous_filter = tempo_filter;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(speed_param, sizeof(speed_param), "%f", qPow(base, speedlog));
|
||||
last_filter = NULL;
|
||||
avfilter_graph_create_filter(&last_filter, avfilter_get_by_name("atempo"), "atempo", speed_param, NULL, clip->filter_graph);
|
||||
avfilter_link(previous_filter, 0, last_filter, 0);
|
||||
}
|
||||
snprintf(speed_param, sizeof(speed_param), "%f", qPow(base, speedlog));
|
||||
last_filter = NULL;
|
||||
avfilter_graph_create_filter(&last_filter, avfilter_get_by_name("atempo"), "atempo", speed_param, NULL, clip->filter_graph);
|
||||
avfilter_link(previous_filter, 0, last_filter, 0);
|
||||
}
|
||||
|
||||
avfilter_link(last_filter, 0, clip->buffersink_ctx, 0);
|
||||
} else {
|
||||
target_sample_rate = qRound64(target_sample_rate / clip->speed);
|
||||
avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0);
|
||||
}
|
||||
avfilter_link(last_filter, 0, clip->buffersink_ctx, 0);
|
||||
} else {
|
||||
target_sample_rate = qRound64(target_sample_rate / clip->speed);
|
||||
avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0);
|
||||
}
|
||||
|
||||
int sample_rates[] = { target_sample_rate, 0 };
|
||||
if (av_opt_set_int_list(clip->buffersink_ctx, "sample_rates", sample_rates, 0, AV_OPT_SEARCH_CHILDREN) < 0) {
|
||||
dout << "[ERROR] Could not set output sample rates";
|
||||
}
|
||||
int sample_rates[] = { target_sample_rate, 0 };
|
||||
if (av_opt_set_int_list(clip->buffersink_ctx, "sample_rates", sample_rates, 0, AV_OPT_SEARCH_CHILDREN) < 0) {
|
||||
dout << "[ERROR] Could not set output sample rates";
|
||||
}
|
||||
|
||||
avfilter_graph_config(clip->filter_graph, NULL);
|
||||
avfilter_graph_config(clip->filter_graph, NULL);
|
||||
|
||||
clip->audio_reset = true;
|
||||
}
|
||||
clip->audio_reset = true;
|
||||
}
|
||||
|
||||
clip->frame = av_frame_alloc();
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_TONE:
|
||||
clip->frame = av_frame_alloc();
|
||||
clip->frame->format = sample_format;
|
||||
clip->frame->channel_layout = clip->sequence->audio_layout;
|
||||
clip->frame->channels = av_get_channel_layout_nb_channels(clip->frame->channel_layout);
|
||||
clip->frame->sample_rate = audio_output->format().sampleRate();
|
||||
clip->frame->nb_samples = 2048;
|
||||
av_frame_make_writable(clip->frame);
|
||||
if (av_frame_get_buffer(clip->frame, 0)) {
|
||||
dout << "[ERROR] Could not allocate buffer for tone clip";
|
||||
}
|
||||
clip->audio_reset = true;
|
||||
break;
|
||||
}
|
||||
clip->frame = av_frame_alloc();
|
||||
}
|
||||
|
||||
for (int i=0;i<clip->effects.size();i++) {
|
||||
clip->effects.at(i)->open();
|
||||
@@ -864,24 +856,23 @@ void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, QV
|
||||
clip->audio_reset = false;
|
||||
}
|
||||
|
||||
switch (clip->media_type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
if (clip->media == NULL) {
|
||||
if (clip->track >= 0) {
|
||||
cache_audio_worker(clip, scrubbing, nests);
|
||||
}
|
||||
} else if (clip->media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
cache_video_worker(clip, playhead);
|
||||
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
cache_audio_worker(clip, scrubbing, nests);
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_TONE:
|
||||
cache_audio_worker(clip, scrubbing, nests);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void close_clip_worker(Clip* clip) {
|
||||
clip->finished_opening = false;
|
||||
|
||||
if (clip->media_type == MEDIA_TYPE_FOOTAGE) {
|
||||
if (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
clip->queue_clear();
|
||||
|
||||
avfilter_graph_free(&clip->filter_graph);
|
||||
|
||||
+43
-48
@@ -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,6 +10,7 @@
|
||||
#include "panels/viewer.h"
|
||||
#include "project/effect.h"
|
||||
#include "panels/effectcontrols.h"
|
||||
#include "project/media.h"
|
||||
#include "debug.h"
|
||||
|
||||
extern "C" {
|
||||
@@ -32,36 +33,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 +76,22 @@ 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:
|
||||
clip->open = false;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -113,7 +108,7 @@ void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<C
|
||||
|
||||
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)));
|
||||
@@ -318,11 +313,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);
|
||||
if (c != NULL) {
|
||||
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
closeActiveClips(c->media->to_sequence(), wait);
|
||||
close_clip(c);
|
||||
} else if (c->media_type == MEDIA_TYPE_FOOTAGE && c->open) {
|
||||
} 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);
|
||||
|
||||
+48
-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,6 @@ Clip::Clip(Sequence* s) :
|
||||
use_existing_frame(false),
|
||||
filter_graph(NULL),
|
||||
fbo(NULL),
|
||||
texture(NULL),
|
||||
opts(NULL)
|
||||
{
|
||||
pkt = av_packet_alloc();
|
||||
@@ -55,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;
|
||||
@@ -93,29 +92,23 @@ void Clip::reset() {
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -166,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();
|
||||
}
|
||||
}
|
||||
@@ -210,36 +203,44 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,28 +248,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;
|
||||
}
|
||||
}
|
||||
@@ -277,16 +268,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;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -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;
|
||||
@@ -42,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);
|
||||
@@ -64,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;
|
||||
|
||||
@@ -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), in(0), out(0) {
|
||||
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 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 "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;
|
||||
case MEDIA_TYPE_FOLDER: 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: return to_footage()->get_stream_from_file_index(true, stream)->video_frame_rate;
|
||||
case MEDIA_TYPE_SEQUENCE: return to_sequence()->frame_rate;
|
||||
}
|
||||
return qSNaN();
|
||||
}
|
||||
|
||||
int Media::get_sampling_rate(int stream) {
|
||||
switch (get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE: 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) r = f->video_tracks.at(0)->video_frame_rate;
|
||||
if (!qIsNull(r)) {
|
||||
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();
|
||||
if (f->video_tracks.size() > 0) {
|
||||
double r = get_frame_rate();
|
||||
if (!qIsNull(r)) 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;
|
||||
}
|
||||
|
||||
Media *Media::takeChild(int i) {
|
||||
Media* ref = children.at(i);
|
||||
children.removeAt(i);
|
||||
return ref;
|
||||
}
|
||||
|
||||
void Media::removeChild(Media* m) {
|
||||
for (int i=0;i<children.size();i++) {
|
||||
if (children.at(i) == m) {
|
||||
children.removeAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#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
|
||||
#define MEDIA_TYPE_SOLID 3
|
||||
#define MEDIA_TYPE_TONE 4
|
||||
|
||||
class 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 = 0);
|
||||
int get_sampling_rate(int stream = 0);
|
||||
|
||||
// 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();
|
||||
Media* takeChild(int i);
|
||||
void removeChild(Media* m);
|
||||
|
||||
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,146 @@
|
||||
#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) {
|
||||
clear();
|
||||
}
|
||||
|
||||
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() {
|
||||
destroy_root();
|
||||
root_item = new Media(0);
|
||||
root_item->root = true;
|
||||
update_data();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
int ProjectModel::topLevelItemCount() {
|
||||
return root_item->childCount();
|
||||
}
|
||||
|
||||
Media *ProjectModel::topLevelItem(int i) {
|
||||
return root_item->child(i);
|
||||
}
|
||||
|
||||
void ProjectModel::addTopLevelItem(Media *m) {
|
||||
root_item->appendChild(m);
|
||||
}
|
||||
|
||||
Media *ProjectModel::takeTopLevelItem(int i) {
|
||||
Media* ref = root_item->child(i);
|
||||
root_item->takeChild(i);
|
||||
return ref;
|
||||
}
|
||||
|
||||
void ProjectModel::removeTopLevelItem(Media *m) {
|
||||
root_item->removeChild(m);
|
||||
}
|
||||
|
||||
void ProjectModel::update_data() {
|
||||
// emit dataChanged();
|
||||
emit layoutChanged();
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#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;
|
||||
int topLevelItemCount();
|
||||
Media* topLevelItem(int i);
|
||||
void addTopLevelItem(Media*);
|
||||
Media* takeTopLevelItem(int i);
|
||||
void removeTopLevelItem(Media* m);
|
||||
void update_data();
|
||||
|
||||
private:
|
||||
Media* root_item;
|
||||
};
|
||||
|
||||
#endif // PROJECTMODEL_H
|
||||
+66
-100
@@ -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;
|
||||
@@ -340,7 +341,7 @@ void DeleteTransitionCommand::redo() {
|
||||
mainWindow->setWindowModified(true);
|
||||
}
|
||||
|
||||
NewSequenceCommand::NewSequenceCommand(QTreeWidgetItem *s, QTreeWidgetItem* iparent) :
|
||||
NewSequenceCommand::NewSequenceCommand(Media *s, Media* iparent) :
|
||||
seq(s),
|
||||
parent(iparent),
|
||||
done(false),
|
||||
@@ -353,7 +354,7 @@ NewSequenceCommand::~NewSequenceCommand() {
|
||||
|
||||
void NewSequenceCommand::undo() {
|
||||
if (parent == NULL) {
|
||||
panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(seq));
|
||||
project_model.removeTopLevelItem(seq);
|
||||
} else {
|
||||
parent->removeChild(seq);
|
||||
}
|
||||
@@ -363,45 +364,38 @@ void NewSequenceCommand::undo() {
|
||||
|
||||
void NewSequenceCommand::redo() {
|
||||
if (parent == NULL) {
|
||||
panel_project->source_table->addTopLevelItem(seq);
|
||||
project_model.addTopLevelItem(seq);
|
||||
} else {
|
||||
parent->addChild(seq);
|
||||
parent->appendChild(seq);
|
||||
}
|
||||
done = true;
|
||||
mainWindow->setWindowModified(true);
|
||||
}
|
||||
|
||||
AddMediaCommand::AddMediaCommand(QTreeWidgetItem* iitem, QTreeWidgetItem* iparent) :
|
||||
AddMediaCommand::AddMediaCommand(Media* iitem, Media* iparent) :
|
||||
item(iitem),
|
||||
parent(iparent),
|
||||
done(false),
|
||||
old_project_changed(mainWindow->isWindowModified())
|
||||
{}
|
||||
{
|
||||
if (parent == NULL) parent = project_model.get_root();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
parent->removeChild(item);
|
||||
done = false;
|
||||
mainWindow->setWindowModified(old_project_changed);
|
||||
}
|
||||
|
||||
void AddMediaCommand::redo() {
|
||||
if (parent == NULL) {
|
||||
panel_project->source_table->addTopLevelItem(item);
|
||||
} else {
|
||||
parent->addChild(item);
|
||||
}
|
||||
parent->appendChild(item);
|
||||
project_model.update_data();
|
||||
|
||||
/* Here we force the source_table to sort itself.
|
||||
*
|
||||
@@ -419,32 +413,33 @@ void AddMediaCommand::redo() {
|
||||
* 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);
|
||||
panel_project->source_table->setSortingEnabled(true);*/
|
||||
|
||||
done = true;
|
||||
mainWindow->setWindowModified(true);
|
||||
}
|
||||
|
||||
DeleteMediaCommand::DeleteMediaCommand(QTreeWidgetItem* i) :
|
||||
DeleteMediaCommand::DeleteMediaCommand(const QModelIndex& i) :
|
||||
item(i),
|
||||
parent(i.parent()),
|
||||
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;
|
||||
Media* m = static_cast<Media*>(item.internalPointer());
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteMediaCommand::undo() {
|
||||
if (parent == NULL) {
|
||||
panel_project->source_table->addTopLevelItem(item);
|
||||
Media* m = static_cast<Media*>(item.internalPointer());
|
||||
if (!parent.isValid()) {
|
||||
project_model.addTopLevelItem(m);
|
||||
} else {
|
||||
parent->addChild(item);
|
||||
static_cast<Media*>(parent.internalPointer())->appendChild(m);
|
||||
}
|
||||
|
||||
mainWindow->setWindowModified(old_project_changed);
|
||||
@@ -452,14 +447,14 @@ void DeleteMediaCommand::undo() {
|
||||
}
|
||||
|
||||
void DeleteMediaCommand::redo() {
|
||||
parent = item->parent();
|
||||
|
||||
if (parent == NULL) {
|
||||
panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(item));
|
||||
Media* m = static_cast<Media*>(item.internalPointer());
|
||||
if (!parent.isValid()) {
|
||||
project_model.removeTopLevelItem(m);
|
||||
} else {
|
||||
parent->removeChild(item);
|
||||
static_cast<Media*>(parent.internalPointer())->removeChild(m);
|
||||
}
|
||||
|
||||
project_model.update_data();
|
||||
mainWindow->setWindowModified(true);
|
||||
done = true;
|
||||
}
|
||||
@@ -601,25 +596,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;
|
||||
}
|
||||
}
|
||||
@@ -643,11 +637,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 +653,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 +661,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 +685,7 @@ void ReplaceClipMediaCommand::undo() {
|
||||
void ReplaceClipMediaCommand::redo() {
|
||||
replace(false);
|
||||
|
||||
update_ui(true);
|
||||
mainWindow->setWindowModified(true);
|
||||
}
|
||||
|
||||
@@ -737,61 +728,40 @@ 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));
|
||||
}
|
||||
}
|
||||
to->removeChild(items.at(i));
|
||||
froms.at(i)->appendChild(items.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));
|
||||
}
|
||||
}
|
||||
parent->removeChild(items.at(i));
|
||||
to->appendChild(items.at(i));
|
||||
}
|
||||
project_model.update_data();
|
||||
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 +1108,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 +1145,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 +1204,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 +1213,7 @@ void UpdateFootageTooltip::undo() {
|
||||
}
|
||||
|
||||
void UpdateFootageTooltip::redo() {
|
||||
update_footage_tooltip(item, media);
|
||||
item->update_tooltip();
|
||||
}
|
||||
|
||||
MoveEffectCommand::MoveEffectCommand() :
|
||||
|
||||
+31
-36
@@ -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"
|
||||
@@ -166,39 +166,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(const QModelIndex& i);
|
||||
~DeleteMediaCommand();
|
||||
void undo();
|
||||
void redo();
|
||||
private:
|
||||
QTreeWidgetItem* item;
|
||||
QTreeWidgetItem* parent;
|
||||
const QModelIndex& item;
|
||||
const QModelIndex& parent;
|
||||
bool old_project_changed;
|
||||
bool done;
|
||||
};
|
||||
@@ -258,29 +258,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 +301,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 +482,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 +494,7 @@ public:
|
||||
int audio_frequency;
|
||||
int audio_layout;
|
||||
private:
|
||||
QTreeWidgetItem* item;
|
||||
Media* item;
|
||||
Sequence* seq;
|
||||
bool old_project_changed;
|
||||
|
||||
@@ -542,12 +538,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 {
|
||||
|
||||
+75
-65
@@ -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,16 +24,16 @@
|
||||
#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(itemChanged(Media*,int)), this, SLOT(item_renamed(Media*)));
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu()));
|
||||
}
|
||||
|
||||
void SourceTable::show_context_menu() {
|
||||
@@ -43,10 +45,14 @@ void SourceTable::show_context_menu() {
|
||||
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 = static_cast<Media*>(selected_items.at(0).internalPointer());
|
||||
|
||||
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()));
|
||||
@@ -69,11 +75,11 @@ void SourceTable::show_context_menu() {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -99,7 +105,7 @@ void SourceTable::show_context_menu() {
|
||||
QAction* delete_action = menu.addAction("Delete");
|
||||
connect(delete_action, SIGNAL(triggered(bool)), panel_project, 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()));
|
||||
}
|
||||
@@ -109,20 +115,19 @@ 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(static_cast<Media*>(selected_items.at(i).internalPointer()));
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -131,7 +136,8 @@ void SourceTable::create_seq_from_selected() {
|
||||
}
|
||||
|
||||
void SourceTable::reveal_in_browser() {
|
||||
Media* m = get_footage_from_tree(selectedItems().at(0));
|
||||
QModelIndexList selected_items = selectionModel()->selectedRows();
|
||||
Footage* m = static_cast<Footage*>(selected_items.at(0).internalPointer());
|
||||
|
||||
#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 = static_cast<Media*>(index.internalPointer());
|
||||
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) {
|
||||
QModelIndexList selected_items = selectionModel()->selectedRows();
|
||||
if (selected_items.size() == 0) {
|
||||
panel_project->import_dialog();
|
||||
} else if (selectedItems().count() == 1) {
|
||||
QTreeWidgetItem* item = selectedItems().at(0);
|
||||
switch (get_type_from_tree(item)) {
|
||||
} else if (selected_items.size() == 1) {
|
||||
Media* item = static_cast<Media*>(selected_items.at(0).internalPointer());
|
||||
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 = static_cast<Media*>(drop_item.internalPointer());
|
||||
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));
|
||||
panel_project->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);
|
||||
panel_project->process_file_list(false, paths, static_cast<Media*>(parent.internalPointer()), NULL);
|
||||
}
|
||||
}
|
||||
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 = static_cast<Media*>(item.internalPointer());
|
||||
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);
|
||||
}
|
||||
|
||||
+7
-6
@@ -1,13 +1,14 @@
|
||||
#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:
|
||||
@@ -20,13 +21,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();
|
||||
|
||||
+32
-40
@@ -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,7 @@
|
||||
#include "ui_timeline.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "project/media.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "project/effect.h"
|
||||
@@ -238,17 +239,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] = static_cast<Media*>(items.at(i).internalPointer());
|
||||
}
|
||||
import_init = true;
|
||||
}
|
||||
@@ -256,13 +254,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;
|
||||
}
|
||||
}
|
||||
@@ -281,12 +273,12 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +301,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,7 +309,7 @@ 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;
|
||||
}
|
||||
@@ -773,7 +765,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 +799,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 +1219,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->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 +1249,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 +1260,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->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 +1341,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 +2041,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 +2089,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 +2217,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 +2633,5 @@ void TimelineWidget::setScroll(int s) {
|
||||
}
|
||||
|
||||
void TimelineWidget::reveal_media() {
|
||||
panel_project->reveal_media(rc_reveal_media);
|
||||
panel_project->reveal_media(rc_reveal_media);
|
||||
}
|
||||
|
||||
+2
-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
|
||||
|
||||
+58
-62
@@ -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,7 @@
|
||||
#include "io/math.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "project/undo.h"
|
||||
#include "project/media.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QAudioOutput>
|
||||
@@ -126,7 +127,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)
|
||||
@@ -378,7 +379,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 +403,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 +420,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 +463,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 +472,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(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();
|
||||
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 +518,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);
|
||||
}
|
||||
|
||||
@@ -686,21 +686,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
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@
|
||||
|
||||
class Viewer;
|
||||
struct Clip;
|
||||
struct MediaStream;
|
||||
struct FootageStream;
|
||||
class QOpenGLFramebufferObject;
|
||||
class Effect;
|
||||
class EffectGizmo;
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
|
||||
bool waveform;
|
||||
Clip* waveform_clip;
|
||||
MediaStream* waveform_ms;
|
||||
FootageStream* waveform_ms;
|
||||
|
||||
bool force_quit;
|
||||
public slots:
|
||||
|
||||
Reference in New Issue
Block a user