massive rewrite of project data system

This commit is contained in:
itsmattkc
2018-12-13 14:29:09 +11:00
parent 6c3197aac6
commit ac1cb9d14f
44 changed files with 2562 additions and 2158 deletions
+19 -4
View File
@@ -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
View File
@@ -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
+14 -16
View File
@@ -7,34 +7,35 @@
#include <QDialogButtonBox>
#include <QTreeWidgetItem>
#include "io/media.h"
#include "project/footage.h"
#include "project/media.h"
#include "panels/project.h"
#include "project/undo.h"
MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, QTreeWidgetItem *i, Media *m) :
MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) :
QDialog(parent),
item(i),
media(m)
item(i)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QGridLayout* grid = new QGridLayout();
setLayout(grid);
if (m->video_tracks.size() > 0) {
Footage* f = item->to_footage();
if (f->video_tracks.size() > 0) {
interlacing_box = new QComboBox();
interlacing_box->addItem("Auto (" + get_interlacing_name(media->video_tracks.at(0)->video_auto_interlacing) + ")");
interlacing_box->addItem("Auto (" + get_interlacing_name(f->video_tracks.at(0)->video_auto_interlacing) + ")");
interlacing_box->addItem(get_interlacing_name(VIDEO_PROGRESSIVE));
interlacing_box->addItem(get_interlacing_name(VIDEO_TOP_FIELD_FIRST));
interlacing_box->addItem(get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST));
interlacing_box->setCurrentIndex((media->video_tracks.at(0)->video_auto_interlacing == media->video_tracks.at(0)->video_interlacing) ? 0 : media->video_tracks.at(0)->video_interlacing + 1);
interlacing_box->setCurrentIndex((f->video_tracks.at(0)->video_auto_interlacing == f->video_tracks.at(0)->video_interlacing) ? 0 : f->video_tracks.at(0)->video_interlacing + 1);
grid->addWidget(new QLabel("Interlacing:"), 0, 0);
grid->addWidget(interlacing_box, 0, 1);
}
name_box = new QLineEdit(m->name);
name_box = new QLineEdit(item->get_name());
grid->addWidget(new QLabel("Name:"), 1, 0);
grid->addWidget(name_box, 1, 1);
@@ -50,22 +51,19 @@ void MediaPropertiesDialog::accept() {
ComboAction* ca = new ComboAction();
//set interlacing
Footage* f = item->to_footage();
if (interlacing_box->currentIndex() > 0) {
ca->append(new SetInt(&media->video_tracks.at(0)->video_interlacing, interlacing_box->currentIndex() - 1));
ca->append(new SetInt(&f->video_tracks.at(0)->video_interlacing, interlacing_box->currentIndex() - 1));
} else {
ca->append(new SetInt(&media->video_tracks.at(0)->video_interlacing, media->video_tracks.at(0)->video_auto_interlacing));
ca->append(new SetInt(&f->video_tracks.at(0)->video_interlacing, f->video_tracks.at(0)->video_auto_interlacing));
}
//set name
MediaRename* mr = new MediaRename();
mr->from = media->name;
mr->item = item;
mr->to = name_box->text();
item->setText(0, name_box->text());
MediaRename* mr = new MediaRename(item, name_box->text());
ca->append(mr);
ca->appendPost(new CloseAllClipsCommand());
ca->appendPost(new UpdateFootageTooltip(item, media));
ca->appendPost(new UpdateFootageTooltip(item));
undo_stack.push(ca);
+4 -5
View File
@@ -3,20 +3,19 @@
#include <QDialog>
struct Media;
struct Footage;
class QComboBox;
class QLineEdit;
class QTreeWidgetItem;
class Media;
class MediaPropertiesDialog : public QDialog {
Q_OBJECT
public:
MediaPropertiesDialog(QWidget *parent, QTreeWidgetItem* i, Media *m);
MediaPropertiesDialog(QWidget *parent, Media* i);
private:
QComboBox* interlacing_box;
QLineEdit* name_box;
QTreeWidgetItem* item;
Media* media;
Media* item;
private slots:
void accept();
};
+2 -2
View File
@@ -4,7 +4,7 @@
#include <QDialog>
class Project;
class QTreeWidgetItem;
class Media;
struct Sequence;
namespace Ui {
@@ -19,7 +19,7 @@ public:
explicit NewSequenceDialog(QWidget *parent = 0);
~NewSequenceDialog();
Sequence* existing_sequence;
QTreeWidgetItem* existing_item;
Media* existing_item;
void set_sequence_name(const QString& s);
protected:
+18 -51
View File
@@ -8,17 +8,18 @@
#include "project/clip.h"
#include "playback/playback.h"
#include "playback/cacher.h"
#include "io/media.h"
#include "project/footage.h"
#include "project/undo.h"
#include "project/media.h"
#include <QVBoxLayout>
#include <QTreeWidget>
#include <QTreeView>
#include <QLabel>
#include <QPushButton>
#include <QMessageBox>
#include <QCheckBox>
ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, SourceTable *table, QTreeWidgetItem *old_media) :
ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, SourceTable *table, Media *old_media) :
QDialog(parent),
source_table(table),
media(old_media)
@@ -29,12 +30,11 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, SourceTable *tab
layout->addWidget(new QLabel("Select which media you want to replace this media's clips with:"));
tree = new QTreeWidget();
tree->setHeaderHidden(true);
tree = new QTreeView();
layout->addWidget(tree);
use_same_media_in_points = new QCheckBox("Keep the same media in points");
use_same_media_in_points = new QCheckBox("Keep the same media in-points");
use_same_media_in_points->setChecked(true);
layout->addWidget(use_same_media_in_points);
@@ -56,36 +56,34 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, SourceTable *tab
setLayout(layout);
copy_tree(NULL, NULL);
tree->setModel(&project_model);
//copy_tree(NULL, NULL);
}
void ReplaceClipMediaDialog::replace() {
if (tree->selectedItems().size() != 1) {
QModelIndexList selected_items = tree->selectionModel()->selectedRows();
if (selected_items.size() != 1) {
QMessageBox::critical(this, "No media selected", "Please select a media to replace with or click 'Cancel'.", QMessageBox::Ok);
} else {
QTreeWidgetItem* selected_item = tree->selectedItems().at(0);
QTreeWidgetItem* new_item = reinterpret_cast<QTreeWidgetItem*>(selected_item->data(0, Qt::UserRole + 1).value<quintptr>());
Media* new_item = static_cast<Media*>(selected_items.at(0).internalPointer());
if (media == new_item) {
QMessageBox::critical(this, "Same media selected", "You selected the same media that you're replacing. Please select a different one or click 'Cancel'.", QMessageBox::Ok);
} else if (get_type_from_tree(new_item) == MEDIA_TYPE_FOLDER) {
} else if (new_item->get_type() == MEDIA_TYPE_FOLDER) {
QMessageBox::critical(this, "Folder selected", "You cannot replace footage with a folder.", QMessageBox::Ok);
} else {
if (get_type_from_tree(new_item) == MEDIA_TYPE_SEQUENCE && sequence == get_sequence_from_tree(new_item)) {
if (new_item->get_type() == MEDIA_TYPE_SEQUENCE && sequence == new_item->to_sequence()) {
QMessageBox::critical(this, "Active sequence selected", "You cannot insert a sequence into itself.", QMessageBox::Ok);
} else {
void* old_media = get_media_from_tree(media);
} else {
ReplaceClipMediaCommand* rcmc = new ReplaceClipMediaCommand(
old_media,
get_media_from_tree(new_item),
get_type_from_tree(media),
get_type_from_tree(new_item),
media,
new_item,
use_same_media_in_points->isChecked()
);
for (int i=0;i<sequence->clips.size();i++) {
Clip* c = sequence->clips.at(i);
if (c != NULL && c->media == old_media) {
if (c != NULL && c->media == media) {
rcmc->clips.append(c);
}
}
@@ -98,34 +96,3 @@ void ReplaceClipMediaDialog::replace() {
}
}
}
void ReplaceClipMediaDialog::copy_tree(QTreeWidgetItem* parent, QTreeWidgetItem* target) {
QVector<QTreeWidgetItem*> items;
if (parent == NULL) {
for (int i=0;i<source_table->topLevelItemCount();i++) {
items.append(source_table->topLevelItem(i));
}
} else {
for (int i=0;i<parent->childCount();i++) {
items.append(parent->child(i));
}
}
for (int i=0;i<items.size();i++) {
QTreeWidgetItem* original = items.at(i);
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText(0, original->text(0));
item->setData(0, Qt::UserRole + 1, reinterpret_cast<quintptr>(original));
if (target == NULL) {
tree->addTopLevelItem(item);
} else {
target->addChild(item);
}
if (original->childCount() > 0) {
copy_tree(original, item);
}
}
}
+6 -7
View File
@@ -4,22 +4,21 @@
#include <QDialog>
class SourceTable;
class QTreeWidget;
class QTreeWidgetItem;
class QTreeView;
class Media;
class QCheckBox;
class ReplaceClipMediaDialog : public QDialog {
Q_OBJECT
public:
ReplaceClipMediaDialog(QWidget* parent, SourceTable* table, QTreeWidgetItem *old_media);
ReplaceClipMediaDialog(QWidget* parent, SourceTable* table, Media *old_media);
private slots:
void replace();
private:
SourceTable* source_table;
QTreeWidget* tree;
QTreeWidgetItem* media;
QCheckBox* use_same_media_in_points;
void copy_tree(QTreeWidgetItem* parent, QTreeWidgetItem *target);
QTreeView* tree;
Media* media;
QCheckBox* use_same_media_in_points;
};
#endif // REPLACECLIPMEDIADIALOG_H
+8 -9
View File
@@ -10,12 +10,13 @@
#include "ui/labelslider.h"
#include "project/clip.h"
#include "project/sequence.h"
#include "io/media.h"
#include "project/footage.h"
#include "playback/playback.h"
#include "panels/panels.h"
#include "panels/timeline.h"
#include "project/undo.h"
#include "project/effect.h"
#include "project/media.h"
SpeedDialog::SpeedDialog(QWidget *parent) : QDialog(parent) {
QVBoxLayout* main_layout = new QVBoxLayout();
@@ -83,14 +84,12 @@ void SpeedDialog::run() {
clip_percent = c->speed;
if (c->track < 0) {
bool process_video = true;
if (c->media_type == MEDIA_TYPE_FOOTAGE) {
Media* m = static_cast<Media*>(c->media);
if (m != NULL) {
MediaStream* ms = m->get_stream_from_file_index(true, c->media_stream);
if (ms != NULL && ms->infinite_length) {
process_video = false;
}
}
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
Footage* m = c->media->to_footage();
FootageStream* ms = m->get_stream_from_file_index(true, c->media_stream);
if (ms != NULL && ms->infinite_length) {
process_video = false;
}
}
if (process_video) {
+1 -1
View File
@@ -12,7 +12,7 @@
#include "ui/collapsiblewidget.h"
#include "project/clip.h"
#include "project/sequence.h"
#include "io/media.h"
#include "project/footage.h"
#include "io/math.h"
#include "ui/labelslider.h"
#include "ui/comboboxex.h"
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+1
View File
@@ -48,5 +48,6 @@
<file>record-disabled.png</file>
<file>transition-tool.png</file>
<file>transition-tool-disabled.png</file>
<file>error.png</file>
</qresource>
</RCC>
+639
View File
@@ -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);
}
+43
View File
@@ -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
View File
@@ -1,6 +1,7 @@
#include "previewgenerator.h"
#include "media.h"
#include "project/media.h"
#include "project/footage.h"
#include "panels/viewer.h"
#include "panels/project.h"
#include "io/config.h"
@@ -28,11 +29,11 @@ extern "C" {
QSemaphore sem(5); // only 5 preview generators can run at one time
PreviewGenerator::PreviewGenerator(QTreeWidgetItem* i, Media* m, bool r) :
PreviewGenerator::PreviewGenerator(Media* i, Footage* m, bool r) :
QThread(0),
fmt_ctx(NULL),
item(i),
media(m),
media(i),
footage(m),
retrieve_duration(false),
contains_still_image(false),
replace(r),
@@ -52,12 +53,12 @@ void PreviewGenerator::parse_media() {
for (int i=0;i<(int)fmt_ctx->nb_streams;i++) {
// Find the decoder for the video stream
if (avcodec_find_decoder(fmt_ctx->streams[i]->codecpar->codec_id) == NULL) {
dout << "[ERROR] Unsupported codec in stream" << i << "of file" << media->name;
dout << "[ERROR] Unsupported codec in stream" << i << "of file" << footage->name;
} else {
MediaStream* ms = media->get_stream_from_file_index(fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, i);
FootageStream* ms = footage->get_stream_from_file_index(fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, i);
bool append = false;
if (ms == NULL) {
ms = new MediaStream();
ms = new FootageStream();
ms->preview_done = false;
ms->file_index = i;
append = true;
@@ -93,18 +94,18 @@ void PreviewGenerator::parse_media() {
ms->video_auto_interlacing = VIDEO_PROGRESSIVE;
ms->video_interlacing = VIDEO_PROGRESSIVE;
if (append) media->video_tracks.append(ms);
if (append) footage->video_tracks.append(ms);
} else if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
ms->audio_channels = fmt_ctx->streams[i]->codecpar->channels;
ms->audio_layout = fmt_ctx->streams[i]->codecpar->channel_layout;
ms->audio_frequency = fmt_ctx->streams[i]->codecpar->sample_rate;
if (append) media->audio_tracks.append(ms);
if (append) footage->audio_tracks.append(ms);
} else if (append) {
delete ms;
}
}
}
media->length = fmt_ctx->duration;
footage->length = fmt_ctx->duration;
if (fmt_ctx->duration == INT64_MIN) {
retrieve_duration = true;
@@ -116,28 +117,29 @@ void PreviewGenerator::parse_media() {
bool PreviewGenerator::retrieve_preview(const QString& hash) {
// returns true if generate_waveform must be run, false if we got all previews from cached files
if (retrieve_duration) {
//dout << "[NOTE] " << media->name << "needs to retrieve duration";
return true;
}
bool found = true;
for (int i=0;i<media->video_tracks.size();i++) {
MediaStream* ms = media->video_tracks.at(i);
for (int i=0;i<footage->video_tracks.size();i++) {
FootageStream* ms = footage->video_tracks.at(i);
QString thumb_path = get_thumbnail_path(hash, ms);
QFile f(thumb_path);
if (f.exists() && ms->video_preview.load(thumb_path)) {
//dout << "loaded thumb" << ms->file_index << "from" << thumb_path;
dout << "loaded thumb" << ms->file_index << "from" << thumb_path;
ms->preview_done = true;
} else {
found = false;
break;
}
}
for (int i=0;i<media->audio_tracks.size();i++) {
MediaStream* ms = media->audio_tracks.at(i);
for (int i=0;i<footage->audio_tracks.size();i++) {
FootageStream* ms = footage->audio_tracks.at(i);
QString waveform_path = get_waveform_path(hash, ms);
QFile f(waveform_path);
if (f.exists()) {
//dout << "loaded wave" << ms->file_index << "from" << waveform_path;
dout << "loaded wave" << ms->file_index << "from" << waveform_path;
f.open(QFile::ReadOnly);
QByteArray data = f.readAll();
ms->audio_preview.resize(data.size());
@@ -153,12 +155,12 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) {
}
}
if (!found) {
for (int i=0;i<media->video_tracks.size();i++) {
MediaStream* ms = media->video_tracks.at(i);
for (int i=0;i<footage->video_tracks.size();i++) {
FootageStream* ms = footage->video_tracks.at(i);
ms->preview_done = false;
}
for (int i=0;i<media->audio_tracks.size();i++) {
MediaStream* ms = media->audio_tracks.at(i);
for (int i=0;i<footage->audio_tracks.size();i++) {
FootageStream* ms = footage->audio_tracks.at(i);
ms->audio_preview.clear();
ms->preview_done = false;
}
@@ -167,11 +169,11 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) {
}
void PreviewGenerator::finalize_media() {
media->ready_lock.unlock();
media->ready = true;
footage->ready_lock.unlock();
footage->ready = true;
if (!cancelled) {
if (media->video_tracks.size() == 0) {
if (footage->video_tracks.size() == 0) {
emit set_icon(ICON_TYPE_AUDIO, replace);
} else if (contains_still_image) {
emit set_icon(ICON_TYPE_IMAGE, replace);
@@ -179,7 +181,7 @@ void PreviewGenerator::finalize_media() {
emit set_icon(ICON_TYPE_VIDEO, replace);
}
if (!contains_still_image || media->audio_tracks.size() > 0) {
/*if (!contains_still_image || media->audio_tracks.size() > 0) {
double frame_rate = 30;
if (!contains_still_image && media->video_tracks.size() > 0) frame_rate = media->video_tracks.at(0)->video_frame_rate;
item->setText(1, frame_to_timecode(media->get_length_in_frames(frame_rate), config.timecode_view, frame_rate));
@@ -189,7 +191,7 @@ void PreviewGenerator::finalize_media() {
} else {
item->setText(2, QString::number(media->audio_tracks.at(0)->audio_frequency) + " Hz");
}
}
}*/
}
}
@@ -229,6 +231,9 @@ void PreviewGenerator::generate_waveform() {
while (codec_ctx[packet->stream_index] == NULL || avcodec_receive_frame(codec_ctx[packet->stream_index], temp_frame) == AVERROR(EAGAIN)) {
av_packet_unref(packet);
int read_ret = av_read_frame(fmt_ctx, packet);
//dout << "read frame for" << footage->name << footage->url << read_ret << "retrieve_duration:" << retrieve_duration << "eof:" << end_of_file << "packet pts:" << packet->pts;
if (read_ret < 0) {
end_of_file = true;
if (read_ret != AVERROR_EOF) dout << "[ERROR] Failed to read packet for preview generation" << read_ret;
@@ -244,7 +249,7 @@ void PreviewGenerator::generate_waveform() {
}
}
if (!end_of_file) {
MediaStream* s = media->get_stream_from_file_index(fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, packet->stream_index);
FootageStream* s = footage->get_stream_from_file_index(fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, packet->stream_index);
if (s != NULL) {
if (fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (!s->preview_done) {
@@ -349,10 +354,10 @@ void PreviewGenerator::generate_waveform() {
// check if we've got all our previews
if (retrieve_duration) {
done = false;
} else if (media->audio_tracks.size() == 0) {
} else if (footage->audio_tracks.size() == 0) {
done = true;
for (int i=0;i<media->video_tracks.size();i++) {
if (!media->video_tracks.at(i)->preview_done) {
for (int i=0;i<footage->video_tracks.size();i++) {
if (!footage->video_tracks.at(i)->preview_done) {
done = false;
break;
}
@@ -365,8 +370,8 @@ void PreviewGenerator::generate_waveform() {
av_packet_unref(packet);
}
}
for (int i=0;i<media->audio_tracks.size();i++) {
media->audio_tracks.at(i)->preview_done = true;
for (int i=0;i<footage->audio_tracks.size();i++) {
footage->audio_tracks.at(i)->preview_done = true;
}
av_frame_free(&temp_frame);
av_packet_free(&packet);
@@ -376,33 +381,33 @@ void PreviewGenerator::generate_waveform() {
}
}
if (retrieve_duration) {
media->length = 0;
footage->length = 0;
int maximum_stream = 0;
for (unsigned int i=0;i<fmt_ctx->nb_streams;i++) {
if (media_lengths[i] > media_lengths[maximum_stream]) {
maximum_stream = i;
}
}
media->length = (double) media_lengths[maximum_stream] / av_q2d(fmt_ctx->streams[maximum_stream]->avg_frame_rate) * AV_TIME_BASE; // TODO redo with PTS
footage->length = (double) media_lengths[maximum_stream] / av_q2d(fmt_ctx->streams[maximum_stream]->avg_frame_rate) * AV_TIME_BASE; // TODO redo with PTS
finalize_media();
}
delete [] media_lengths;
delete [] codec_ctx;
}
QString PreviewGenerator::get_thumbnail_path(const QString& hash, MediaStream* ms) {
QString PreviewGenerator::get_thumbnail_path(const QString& hash, FootageStream* ms) {
return data_path + "/" + hash + "t" + QString::number(ms->file_index);
}
QString PreviewGenerator::get_waveform_path(const QString& hash, MediaStream* ms) {
QString PreviewGenerator::get_waveform_path(const QString& hash, FootageStream* ms) {
return data_path + "/" + hash + "w" + QString::number(ms->file_index);
}
void PreviewGenerator::run() {
Q_ASSERT(footage != NULL);
Q_ASSERT(media != NULL);
Q_ASSERT(item != NULL);
QByteArray ba = media->url.toLatin1();
QByteArray ba = footage->url.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() {
+8 -7
View File
@@ -2,22 +2,23 @@
#define PREVIEWGENERATOR_H
#include <QThread>
#include <QSemaphore>
#define ICON_TYPE_VIDEO 0
#define ICON_TYPE_AUDIO 1
#define ICON_TYPE_IMAGE 2
#define ICON_TYPE_ERROR 3
struct Media;
struct MediaStream;
struct Footage;
struct FootageStream;
struct AVFormatContext;
class QTreeWidgetItem;
class Media;
class PreviewGenerator : public QThread
{
Q_OBJECT
public:
PreviewGenerator(QTreeWidgetItem*, Media*, bool);
PreviewGenerator(Media*, Footage*, bool);
void run();
void cancel();
signals:
@@ -28,15 +29,15 @@ private:
void generate_waveform();
void finalize_media();
AVFormatContext* fmt_ctx;
QTreeWidgetItem* item;
Media* media;
Footage* footage;
bool retrieve_duration;
bool contains_still_image;
bool replace;
bool cancelled;
QString data_path;
QString get_thumbnail_path(const QString &hash, MediaStream* ms);
QString get_waveform_path(const QString& hash, MediaStream* ms);
QString get_thumbnail_path(const QString &hash, FootageStream* ms);
QString get_waveform_path(const QString& hash, FootageStream* ms);
};
#endif // PREVIEWGENERATOR_H
+5 -8
View File
@@ -3,11 +3,12 @@
#include "io/config.h"
#include "io/path.h"
#include "io/media.h"
#include "project/footage.h"
#include "project/sequence.h"
#include "project/clip.h"
#include "project/undo.h"
#include "project/media.h"
#include "ui/sourcetable.h"
#include "ui/viewerwidget.h"
@@ -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);
+10 -4
View File
@@ -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 \
+2
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+37 -42
View File
@@ -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
View File
@@ -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
View File
@@ -14,10 +14,11 @@
#include "playback/playback.h"
#include "ui_viewer.h"
#include "project/undo.h"
#include "project/media.h"
#include "io/config.h"
#include "project/effect.h"
#include "project/transition.h"
#include "io/media.h"
#include "project/footage.h"
#include "io/clipboard.h"
#include "debug.h"
@@ -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
View File
@@ -22,12 +22,13 @@ class SourceTable;
class ViewerWidget;
class ComboAction;
class Effect;
class Media;
class Transition;
struct EffectMeta;
struct Sequence;
struct Clip;
struct Media;
struct MediaStream;
struct Footage;
struct FootageStream;
long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate);
int getScreenPointFromFrame(double zoom, long frame);
@@ -49,8 +50,7 @@ struct Ghost {
long old_clip_in;
// importing variables
void* media;
int media_type;
Media* media;
int media_stream;
// other variables
@@ -102,7 +102,7 @@ public:
void next_cut();
void toggle_show_all();
void create_ghosts_from_media(Sequence *seq, long entry_point, QVector<void *> &media_list, QVector<int> &type_list);
void create_ghosts_from_media(Sequence *seq, long entry_point, QVector<Media *> &media_list);
void add_clips_from_ghosts(ComboAction *ca, Sequence *s);
int getTimelineScreenPointFromFrame(long frame);
+78 -76
View File
@@ -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
View File
@@ -6,6 +6,7 @@
class Timeline;
class ViewerWidget;
class Media;
struct Sequence;
namespace Ui {
@@ -26,7 +27,7 @@ public:
bool is_focused();
void set_main_sequence();
void set_media(int type, void* media);
void set_media(Media *m);
void compose();
void set_playpause_icon(bool play);
void update_playhead_timecode(long p);
@@ -65,6 +66,7 @@ public:
ViewerWidget* viewer_widget;
Media* media;
Sequence* seq;
Ui::Viewer *ui;
+393 -402
View File
@@ -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
View File
@@ -2,7 +2,7 @@
#include "project/clip.h"
#include "project/sequence.h"
#include "io/media.h"
#include "project/footage.h"
#include "playback/audio.h"
#include "playback/cacher.h"
#include "panels/panels.h"
@@ -10,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();
}
+1
View File
@@ -12,6 +12,7 @@ struct AVFrame;
extern bool texture_failed;
extern bool rendering;
bool clip_uses_cacher(Clip* clip);
void open_clip(Clip* clip, bool multithreaded);
void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip *> &nests);
void close_clip(Clip* clip);
+48 -57
View File
@@ -2,13 +2,14 @@
#include "project/effect.h"
#include "project/transition.h"
#include "io/media.h"
#include "project/footage.h"
#include "io/config.h"
#include "playback/playback.h"
#include "playback/cacher.h"
#include "panels/project.h"
#include "project/sequence.h"
#include "panels/timeline.h"
#include "project/media.h"
#include "undo.h"
extern "C" {
@@ -35,7 +36,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
View File
@@ -13,9 +13,10 @@ class Effect;
class Transition;
class QOpenGLFramebufferObject;
class ComboAction;
class Media;
struct Sequence;
struct Media;
struct MediaStream;
struct Footage;
struct FootageStream;
struct AVFormatContext;
struct AVStream;
@@ -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;
+8 -7
View File
@@ -1,4 +1,4 @@
#include "media.h"
#include "footage.h"
#include <QDebug>
#include <QtMath>
@@ -10,15 +10,15 @@ extern "C" {
#include "project/clip.h"
Media::Media() : ready(false), preview_gen(NULL), invalid(false), 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) {
+10 -16
View File
@@ -1,5 +1,5 @@
#ifndef MEDIA_H
#define MEDIA_H
#ifndef FOOTAGE_H
#define FOOTAGE_H
#include <QString>
#include <QVector>
@@ -8,12 +8,6 @@
#include <QMutex>
#include <QPixmap>
#define MEDIA_TYPE_FOOTAGE 0
#define MEDIA_TYPE_SEQUENCE 1
#define MEDIA_TYPE_FOLDER 2
#define MEDIA_TYPE_SOLID 3
#define MEDIA_TYPE_TONE 4
#define VIDEO_PROGRESSIVE 0
#define VIDEO_TOP_FIELD_FIRST 1
#define VIDEO_BOTTOM_FIELD_FIRST 2
@@ -23,7 +17,7 @@ struct Clip;
class PreviewGenerator;
class MediaThrobber;
struct MediaStream {
struct FootageStream {
int file_index;
int video_width;
int video_height;
@@ -41,15 +35,15 @@ struct MediaStream {
QVector<char> audio_preview;
};
struct Media {
Media();
~Media();
struct Footage {
Footage();
~Footage();
QString url;
QString name;
int64_t length;
QVector<MediaStream*> video_tracks;
QVector<MediaStream*> audio_tracks;
QVector<FootageStream*> video_tracks;
QVector<FootageStream*> audio_tracks;
int save_id;
bool ready;
bool invalid;
@@ -62,8 +56,8 @@ struct Media {
long out;
long get_length_in_frames(double frame_rate);
MediaStream* get_stream_from_file_index(bool video, int index);
FootageStream* get_stream_from_file_index(bool video, int index);
void reset();
};
#endif // MEDIA_H
#endif // FOOTAGE_H
+312
View File
@@ -0,0 +1,312 @@
#include "media.h"
#include "footage.h"
#include "sequence.h"
#include "undo.h"
#include "io/config.h"
#include "panels/viewer.h"
#include "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;
}
}
}
+67
View File
@@ -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
+146
View File
@@ -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();
}
+39
View File
@@ -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
View File
@@ -15,13 +15,14 @@
#include "ui/sourcetable.h"
#include "project/effect.h"
#include "project/transition.h"
#include "io/media.h"
#include "project/footage.h"
#include "playback/cacher.h"
#include "ui/labelslider.h"
#include "ui/viewerwidget.h"
#include "project/marker.h"
#include "mainwindow.h"
#include "io/clipboard.h"
#include "project/media.h"
#include "debug.h"
QUndoStack undo_stack;
@@ -169,7 +170,7 @@ void SetTimelineInOutCommand::undo() {
// footage viewer functions
if (seq->wrapper_sequence) {
Media* m = static_cast<Media*>(seq->clips.at(0)->media);
Footage* m = seq->clips.at(0)->media->to_footage();
m->using_inout = old_enabled;
m->in = old_in;
m->out = old_out;
@@ -189,7 +190,7 @@ void SetTimelineInOutCommand::redo() {
// footage viewer functions
if (seq->wrapper_sequence) {
Media* m = static_cast<Media*>(seq->clips.at(0)->media);
Footage* m = seq->clips.at(0)->media->to_footage();
m->using_inout = new_enabled;
m->in = new_in;
m->out = new_out;
@@ -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 Modelviewviewmodel instead,
* which I'll probably have to switch to soon anyway. So perhaps
* this will be a non-issue soon.
*/
panel_project->source_table->setSortingEnabled(false);
panel_project->source_table->setSortingEnabled(true);
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
View File
@@ -1,7 +1,7 @@
#ifndef UNDO_H
#define UNDO_H
class QTreeWidgetItem;
class Media;
class QCheckBox;
class LabelSlider;
class Effect;
@@ -12,7 +12,7 @@ class Transition;
class EffectGizmo;
struct Clip;
struct Sequence;
struct Media;
struct Footage;
struct EffectMeta;
#include "project/marker.h"
@@ -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
View File
@@ -1,7 +1,7 @@
#include "sourcetable.h"
#include "panels/project.h"
#include "io/media.h"
#include "project/footage.h"
#include "panels/timeline.h"
#include "panels/viewer.h"
#include "panels/panels.h"
@@ -10,6 +10,8 @@
#include "project/sequence.h"
#include "mainwindow.h"
#include "io/config.h"
#include "project/media.h"
#include "debug.h"
#include <QDragEnterEvent>
#include <QMimeData>
@@ -22,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
View File
@@ -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
View File
@@ -8,7 +8,7 @@
#include "project/clip.h"
#include "panels/project.h"
#include "panels/timeline.h"
#include "io/media.h"
#include "project/footage.h"
#include "ui/sourcetable.h"
#include "panels/effectcontrols.h"
#include "panels/viewer.h"
@@ -16,6 +16,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
View File
@@ -13,7 +13,7 @@
struct Sequence;
struct Clip;
struct MediaStream;
struct FootageStream;
class Timeline;
class TimelineAction;
class QScrollBar;
@@ -21,7 +21,7 @@ class SetSelectionsCommand;
class QPainter;
bool same_sign(int a, int b);
void draw_waveform(Clip* clip, MediaStream* ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom);
void draw_waveform(Clip* clip, FootageStream* ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom);
class TimelineWidget : public QWidget {
Q_OBJECT
+58 -62
View File
@@ -10,7 +10,7 @@
#include "project/transition.h"
#include "playback/playback.h"
#include "playback/audio.h"
#include "io/media.h"
#include "project/footage.h"
#include "ui_timeline.h"
#include "playback/cacher.h"
#include "io/config.h"
@@ -18,6 +18,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
View File
@@ -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: