folder support added
This commit is contained in:
@@ -4,8 +4,8 @@
|
||||
#include <QLabel>
|
||||
#include <QtMath>
|
||||
#include <QDebug>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "playback/audio.h"
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
|
||||
|
||||
+1
-3
@@ -6,9 +6,7 @@ extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
Media::Media() {
|
||||
|
||||
}
|
||||
Media::Media() {}
|
||||
|
||||
Media::~Media() {
|
||||
for (int i=0;i<video_tracks.size();i++) {
|
||||
|
||||
+1
-8
@@ -8,11 +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
|
||||
|
||||
struct Sequence;
|
||||
|
||||
struct MediaStream {
|
||||
@@ -34,12 +29,10 @@ struct Media
|
||||
~Media();
|
||||
|
||||
QString url;
|
||||
QString name;
|
||||
char type;
|
||||
QString name;
|
||||
int64_t length;
|
||||
QVector<MediaStream*> video_tracks;
|
||||
QVector<MediaStream*> audio_tracks;
|
||||
Sequence* sequence;
|
||||
int save_id;
|
||||
long get_length_in_frames(float frame_rate);
|
||||
MediaStream* get_stream_from_file_index(int index);
|
||||
|
||||
+143
-151
@@ -26,170 +26,162 @@ PreviewGenerator::PreviewGenerator(QObject* parent) : QThread(parent) {
|
||||
#define WAVEFORM_RESOLUTION 64
|
||||
|
||||
void PreviewGenerator::run() {
|
||||
if (media == NULL) {
|
||||
qDebug() << "[ERROR] No media was set for preview generation";
|
||||
} else {
|
||||
if (media->type == MEDIA_TYPE_SEQUENCE) {
|
||||
qDebug() << "[ERROR] Cannot run preview generation on a sequence";
|
||||
} else {
|
||||
SwsContext* sws_ctx;
|
||||
SwrContext* swr_ctx;
|
||||
AVFrame* temp_frame = av_frame_alloc();
|
||||
AVCodecContext** codec_ctx = new AVCodecContext* [fmt_ctx->nb_streams];
|
||||
for (unsigned int i=0;i<fmt_ctx->nb_streams;i++) {
|
||||
codec_ctx[i] = NULL;
|
||||
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
AVCodec* codec = avcodec_find_decoder(fmt_ctx->streams[i]->codecpar->codec_id);
|
||||
codec_ctx[i] = avcodec_alloc_context3(codec);
|
||||
avcodec_parameters_to_context(codec_ctx[i], fmt_ctx->streams[i]->codecpar);
|
||||
avcodec_open2(codec_ctx[i], codec, NULL);
|
||||
Q_ASSERT(media != NULL);
|
||||
|
||||
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && codec_ctx[i]->channel_layout == 0) {
|
||||
codec_ctx[i]->channel_layout = guess_layout_from_channels(fmt_ctx->streams[i]->codecpar->channels);
|
||||
}
|
||||
SwsContext* sws_ctx;
|
||||
SwrContext* swr_ctx;
|
||||
AVFrame* temp_frame = av_frame_alloc();
|
||||
AVCodecContext** codec_ctx = new AVCodecContext* [fmt_ctx->nb_streams];
|
||||
for (unsigned int i=0;i<fmt_ctx->nb_streams;i++) {
|
||||
codec_ctx[i] = NULL;
|
||||
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
AVCodec* codec = avcodec_find_decoder(fmt_ctx->streams[i]->codecpar->codec_id);
|
||||
codec_ctx[i] = avcodec_alloc_context3(codec);
|
||||
avcodec_parameters_to_context(codec_ctx[i], fmt_ctx->streams[i]->codecpar);
|
||||
avcodec_open2(codec_ctx[i], codec, NULL);
|
||||
|
||||
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && codec_ctx[i]->channel_layout == 0) {
|
||||
codec_ctx[i]->channel_layout = guess_layout_from_channels(fmt_ctx->streams[i]->codecpar->channels);
|
||||
}
|
||||
}
|
||||
}
|
||||
AVPacket packet;
|
||||
bool done = true;
|
||||
|
||||
bool end_of_file = false;
|
||||
|
||||
// get the ball rolling
|
||||
av_read_frame(fmt_ctx, &packet);
|
||||
avcodec_send_packet(codec_ctx[packet.stream_index], &packet);
|
||||
|
||||
while (!end_of_file) {
|
||||
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);
|
||||
if (read_ret < 0) {
|
||||
end_of_file = true;
|
||||
if (read_ret != AVERROR_EOF) qDebug() << "[ERROR] Failed to read packet for preview generation";
|
||||
break;
|
||||
} else if (codec_ctx[packet.stream_index] != NULL) {
|
||||
int send_ret = avcodec_send_packet(codec_ctx[packet.stream_index], &packet);
|
||||
if (send_ret < 0) {
|
||||
qDebug() << "[ERROR] Failed to send packet for preview generation - aborting";
|
||||
end_of_file = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
AVPacket packet;
|
||||
bool done = true;
|
||||
}
|
||||
if (!end_of_file) {
|
||||
MediaStream* s = media->get_stream_from_file_index(packet.stream_index);
|
||||
if (s != NULL && !s->preview_done) {
|
||||
if (fmt_ctx->streams[packet.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
int dstH = 120;
|
||||
int dstW = dstH * ((float)temp_frame->width/(float)temp_frame->height);
|
||||
uint8_t* data = new uint8_t[dstW*dstH*3];
|
||||
|
||||
bool end_of_file = false;
|
||||
sws_ctx = sws_getContext(
|
||||
temp_frame->width,
|
||||
temp_frame->height,
|
||||
static_cast<AVPixelFormat>(temp_frame->format),
|
||||
dstW,
|
||||
dstH,
|
||||
static_cast<AVPixelFormat>(AV_PIX_FMT_RGB24),
|
||||
SWS_FAST_BILINEAR,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
// get the ball rolling
|
||||
av_read_frame(fmt_ctx, &packet);
|
||||
avcodec_send_packet(codec_ctx[packet.stream_index], &packet);
|
||||
int linesize[AV_NUM_DATA_POINTERS];
|
||||
linesize[0] = dstW*3;
|
||||
sws_scale(sws_ctx, temp_frame->data, temp_frame->linesize, 0, temp_frame->height, &data, linesize);
|
||||
|
||||
while (!end_of_file) {
|
||||
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);
|
||||
if (read_ret < 0) {
|
||||
end_of_file = true;
|
||||
if (read_ret != AVERROR_EOF) qDebug() << "[ERROR] Failed to read packet for preview generation";
|
||||
break;
|
||||
} else if (codec_ctx[packet.stream_index] != NULL) {
|
||||
int send_ret = avcodec_send_packet(codec_ctx[packet.stream_index], &packet);
|
||||
if (send_ret < 0) {
|
||||
qDebug() << "[ERROR] Failed to send packet for preview generation - aborting";
|
||||
end_of_file = true;
|
||||
s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGB888);
|
||||
|
||||
// delete [] data;
|
||||
|
||||
s->preview_done = true;
|
||||
|
||||
sws_freeContext(sws_ctx);
|
||||
|
||||
avcodec_close(codec_ctx[packet.stream_index]);
|
||||
codec_ctx[packet.stream_index] = NULL;
|
||||
} else if (fmt_ctx->streams[packet.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
int interval = qFloor((temp_frame->sample_rate/WAVEFORM_RESOLUTION)/4)*4;
|
||||
|
||||
AVFrame* swr_frame = av_frame_alloc();
|
||||
swr_frame->channel_layout = temp_frame->channel_layout;
|
||||
swr_frame->sample_rate = temp_frame->sample_rate;
|
||||
swr_frame->format = AV_SAMPLE_FMT_S16;
|
||||
|
||||
swr_ctx = swr_alloc_set_opts(
|
||||
NULL,
|
||||
temp_frame->channel_layout,
|
||||
static_cast<AVSampleFormat>(swr_frame->format),
|
||||
temp_frame->sample_rate,
|
||||
temp_frame->channel_layout,
|
||||
static_cast<AVSampleFormat>(temp_frame->format),
|
||||
temp_frame->sample_rate,
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
|
||||
swr_init(swr_ctx);
|
||||
|
||||
swr_convert_frame(swr_ctx, swr_frame, temp_frame);
|
||||
|
||||
int channel_skip = swr_frame->channels * 2;
|
||||
for (int i=0;i<swr_frame->nb_samples;i+=interval) {
|
||||
for (int j=0;j<swr_frame->channels;j++) {
|
||||
qint16 min = 0;
|
||||
qint16 max = 0;
|
||||
for (int k=j*2;k<interval;k+=channel_skip) {
|
||||
qint16 sample = ((swr_frame->data[0][i+k+1] << 8) | swr_frame->data[0][i+k]);
|
||||
if (sample > max) {
|
||||
max = sample;
|
||||
} else if (sample < min) {
|
||||
min = sample;
|
||||
}
|
||||
}
|
||||
min /= 256;
|
||||
max /= 256;
|
||||
s->audio_preview.append(min);
|
||||
s->audio_preview.append(max);
|
||||
}
|
||||
}
|
||||
|
||||
swr_free(&swr_ctx);
|
||||
av_frame_free(&swr_frame);
|
||||
}
|
||||
|
||||
// check if we've got all our previews
|
||||
if (media->audio_tracks.size() == 0) {
|
||||
done = true;
|
||||
for (int i=0;i<media->video_tracks.size();i++) {
|
||||
if (!media->video_tracks.at(i)->preview_done) {
|
||||
done = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!end_of_file) {
|
||||
MediaStream* s = media->get_stream_from_file_index(packet.stream_index);
|
||||
if (s != NULL && !s->preview_done) {
|
||||
if (fmt_ctx->streams[packet.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
int dstH = 120;
|
||||
int dstW = dstH * ((float)temp_frame->width/(float)temp_frame->height);
|
||||
uint8_t* data = new uint8_t[dstW*dstH*3];
|
||||
|
||||
sws_ctx = sws_getContext(
|
||||
temp_frame->width,
|
||||
temp_frame->height,
|
||||
static_cast<AVPixelFormat>(temp_frame->format),
|
||||
dstW,
|
||||
dstH,
|
||||
static_cast<AVPixelFormat>(AV_PIX_FMT_RGB24),
|
||||
SWS_FAST_BILINEAR,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
int linesize[AV_NUM_DATA_POINTERS];
|
||||
linesize[0] = dstW*3;
|
||||
sws_scale(sws_ctx, temp_frame->data, temp_frame->linesize, 0, temp_frame->height, &data, linesize);
|
||||
|
||||
s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGB888);
|
||||
|
||||
// delete [] data;
|
||||
|
||||
s->preview_done = true;
|
||||
|
||||
sws_freeContext(sws_ctx);
|
||||
|
||||
avcodec_close(codec_ctx[packet.stream_index]);
|
||||
codec_ctx[packet.stream_index] = NULL;
|
||||
} else if (fmt_ctx->streams[packet.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
int interval = qFloor((temp_frame->sample_rate/WAVEFORM_RESOLUTION)/4)*4;
|
||||
|
||||
AVFrame* swr_frame = av_frame_alloc();
|
||||
swr_frame->channel_layout = temp_frame->channel_layout;
|
||||
swr_frame->sample_rate = temp_frame->sample_rate;
|
||||
swr_frame->format = AV_SAMPLE_FMT_S16;
|
||||
|
||||
swr_ctx = swr_alloc_set_opts(
|
||||
NULL,
|
||||
temp_frame->channel_layout,
|
||||
static_cast<AVSampleFormat>(swr_frame->format),
|
||||
temp_frame->sample_rate,
|
||||
temp_frame->channel_layout,
|
||||
static_cast<AVSampleFormat>(temp_frame->format),
|
||||
temp_frame->sample_rate,
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
|
||||
swr_init(swr_ctx);
|
||||
|
||||
swr_convert_frame(swr_ctx, swr_frame, temp_frame);
|
||||
|
||||
int channel_skip = swr_frame->channels * 2;
|
||||
for (int i=0;i<swr_frame->nb_samples;i+=interval) {
|
||||
for (int j=0;j<swr_frame->channels;j++) {
|
||||
qint16 min = 0;
|
||||
qint16 max = 0;
|
||||
for (int k=j*2;k<interval;k+=channel_skip) {
|
||||
qint16 sample = ((swr_frame->data[0][i+k+1] << 8) | swr_frame->data[0][i+k]);
|
||||
if (sample > max) {
|
||||
max = sample;
|
||||
} else if (sample < min) {
|
||||
min = sample;
|
||||
}
|
||||
}
|
||||
min /= 256;
|
||||
max /= 256;
|
||||
s->audio_preview.append(min);
|
||||
s->audio_preview.append(max);
|
||||
}
|
||||
}
|
||||
|
||||
swr_free(&swr_ctx);
|
||||
av_frame_free(&swr_frame);
|
||||
}
|
||||
|
||||
// check if we've got all our previews
|
||||
if (media->audio_tracks.size() == 0) {
|
||||
done = true;
|
||||
for (int i=0;i<media->video_tracks.size();i++) {
|
||||
if (!media->video_tracks.at(i)->preview_done) {
|
||||
done = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
end_of_file = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
end_of_file = true;
|
||||
break;
|
||||
}
|
||||
av_packet_unref(&packet);
|
||||
}
|
||||
}
|
||||
for (int i=0;i<media->audio_tracks.size();i++) {
|
||||
media->audio_tracks.at(i)->preview_done = true;
|
||||
}
|
||||
av_frame_free(&temp_frame);
|
||||
for (unsigned int i=0;i<fmt_ctx->nb_streams;i++) {
|
||||
if (codec_ctx[i] != NULL) {
|
||||
avcodec_close(codec_ctx[i]);
|
||||
}
|
||||
}
|
||||
delete [] codec_ctx;
|
||||
av_packet_unref(&packet);
|
||||
}
|
||||
}
|
||||
|
||||
if (fmt_ctx != NULL) {
|
||||
avformat_close_input(&fmt_ctx);
|
||||
for (int i=0;i<media->audio_tracks.size();i++) {
|
||||
media->audio_tracks.at(i)->preview_done = true;
|
||||
}
|
||||
av_frame_free(&temp_frame);
|
||||
for (unsigned int i=0;i<fmt_ctx->nb_streams;i++) {
|
||||
if (codec_ctx[i] != NULL) {
|
||||
avcodec_close(codec_ctx[i]);
|
||||
}
|
||||
}
|
||||
delete [] codec_ctx;
|
||||
|
||||
avformat_close_input(&fmt_ctx);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <QFileDialog>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
#include <QCloseEvent>
|
||||
|
||||
#define OLIVE_FILE_FILTER "Olive Project (*.ove)"
|
||||
|
||||
@@ -304,6 +305,14 @@ bool MainWindow::can_close_project() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *e) {
|
||||
if (can_close_project()) {
|
||||
e->accept();
|
||||
} else {
|
||||
e->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_action_Save_Project_triggered()
|
||||
{
|
||||
save_project();
|
||||
@@ -531,3 +540,8 @@ void MainWindow::on_actionSlide_Tool_triggered()
|
||||
{
|
||||
if (panel_timeline->focused()) panel_timeline->ui->toolSlideButton->click();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionFolder_triggered()
|
||||
{
|
||||
panel_project->new_folder();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *);
|
||||
|
||||
private slots:
|
||||
void on_action_Import_triggered();
|
||||
|
||||
@@ -139,6 +142,8 @@ private slots:
|
||||
|
||||
void on_actionSlide_Tool_triggered();
|
||||
|
||||
void on_actionFolder_triggered();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
void setup_layout();
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<addaction name="actionProject"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSequence"/>
|
||||
<addaction name="actionFolder"/>
|
||||
</widget>
|
||||
<addaction name="menu_New"/>
|
||||
<addaction name="action_Open_Project"/>
|
||||
@@ -583,6 +584,11 @@
|
||||
<string>U</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFolder">
|
||||
<property name="text">
|
||||
<string>Folder</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 4.6.2, 2018-07-10T01:01:52. -->
|
||||
<!-- Written by QtCreator 4.6.2, 2018-07-10T13:36:23. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
+74
-39
@@ -38,7 +38,7 @@ Project::Project(QWidget *parent) :
|
||||
ui(new Ui::Project)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
source_table = ui->treeWidget;
|
||||
connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(rename_media(QTreeWidgetItem*,int)));
|
||||
}
|
||||
|
||||
@@ -70,35 +70,30 @@ QString Project::get_next_sequence_name() {
|
||||
}
|
||||
|
||||
void Project::rename_media(QTreeWidgetItem* item, int column) {
|
||||
Media* m = get_media_from_tree(item);
|
||||
m->name = item->text(column);
|
||||
if (m->type == MEDIA_TYPE_SEQUENCE) {
|
||||
m->sequence->name = m->name;
|
||||
int type = get_type_from_tree(item);
|
||||
QString n = item->text(column);
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOOTAGE: get_media_from_tree(item)->name = n; break;
|
||||
case MEDIA_TYPE_SEQUENCE: get_sequence_from_tree(item)->name = n; break;
|
||||
}
|
||||
}
|
||||
|
||||
void Project::duplicate_selected() {
|
||||
QList<QTreeWidgetItem*> items = ui->treeWidget->selectedItems();
|
||||
for (int i=0;i<items.size();i++) {
|
||||
Media* m = get_media_from_tree(items.at(i));
|
||||
if (m->type == MEDIA_TYPE_SEQUENCE) {
|
||||
new_sequence(m->sequence->copy(), false);
|
||||
int type = get_type_from_tree(items.at(i));
|
||||
if (type == MEDIA_TYPE_SEQUENCE) {
|
||||
new_sequence(get_sequence_from_tree(items.at(i))->copy(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Project::new_sequence(Sequence *s, bool open) {
|
||||
Media* m = new Media();
|
||||
m->type = MEDIA_TYPE_SEQUENCE;
|
||||
m->sequence = s;
|
||||
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem();
|
||||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||
QTreeWidgetItem* item = new_item();
|
||||
item->setText(0, s->name);
|
||||
set_media_of_tree(item, m);
|
||||
set_sequence_of_tree(item, s);
|
||||
|
||||
ui->treeWidget->addTopLevelItem(item);
|
||||
source_table = ui->treeWidget;
|
||||
|
||||
project_changed = true;
|
||||
|
||||
@@ -128,7 +123,6 @@ Media* Project::import_file(QString file) {
|
||||
av_dump_format(pFormatCtx, 0, filename, 0);
|
||||
|
||||
m = new Media();
|
||||
m->type = MEDIA_TYPE_FOOTAGE;
|
||||
m->url = file;
|
||||
m->name = file.mid(file.lastIndexOf('/')+1);
|
||||
|
||||
@@ -155,8 +149,7 @@ Media* Project::import_file(QString file) {
|
||||
}
|
||||
m->length = pFormatCtx->duration;
|
||||
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem();
|
||||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||
QTreeWidgetItem* item = new_item();
|
||||
if (m->video_tracks.size() == 0) {
|
||||
item->setIcon(0, QIcon(":/icons/audiosource.png"));
|
||||
} else {
|
||||
@@ -169,23 +162,39 @@ Media* Project::import_file(QString file) {
|
||||
ui->treeWidget->addTopLevelItem(item);
|
||||
|
||||
project_changed = true;
|
||||
|
||||
// generate waveform/thumbnail in another thread
|
||||
PreviewGenerator* pg = new PreviewGenerator();
|
||||
pg->fmt_ctx = pFormatCtx; // cleaned up in PG
|
||||
pg->media = m;
|
||||
connect(pg, SIGNAL(finished()), pg, SLOT(deleteLater()));
|
||||
pg->start(QThread::LowPriority);
|
||||
}
|
||||
}
|
||||
|
||||
PreviewGenerator* pg = new PreviewGenerator();
|
||||
pg->fmt_ctx = pFormatCtx; // cleaned up in PG
|
||||
pg->media = m;
|
||||
connect(pg, SIGNAL(finished()), pg, SLOT(deleteLater()));
|
||||
pg->start(QThread::LowPriority);
|
||||
|
||||
delete [] filename;
|
||||
return m;
|
||||
}
|
||||
|
||||
QTreeWidgetItem* Project::new_item() {
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem();
|
||||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||
return item;
|
||||
}
|
||||
|
||||
bool Project::is_focused() {
|
||||
return ui->treeWidget->hasFocus();
|
||||
}
|
||||
|
||||
void Project::new_folder() {
|
||||
QTreeWidgetItem* item = new_item();
|
||||
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
|
||||
item->setText(0, "New Folder");
|
||||
set_item_to_folder(item);
|
||||
ui->treeWidget->addTopLevelItem(item);
|
||||
project_changed = true;
|
||||
}
|
||||
|
||||
void Project::delete_selected_media() {
|
||||
QList<QTreeWidgetItem*> items = ui->treeWidget->selectedItems();
|
||||
bool remove = true;
|
||||
@@ -193,8 +202,9 @@ void Project::delete_selected_media() {
|
||||
|
||||
// check if media is in use
|
||||
for (int i=0;i<items.size();i++) {
|
||||
Media* m = get_media_from_tree(items.at(i));
|
||||
if (m->type == MEDIA_TYPE_FOOTAGE && sequence != NULL) {
|
||||
QTreeWidgetItem* item = items.at(i);
|
||||
Media* m = get_media_from_tree(item);
|
||||
if (get_type_from_tree(item) == MEDIA_TYPE_FOOTAGE && sequence != NULL) {
|
||||
for (int j=0;j<sequence->clip_count();j++) {
|
||||
Clip* c = sequence->get_clip(j);
|
||||
if (c != NULL && c->media == m) {
|
||||
@@ -292,23 +302,47 @@ void Project::import_dialog() {
|
||||
process_file_list(files);
|
||||
}
|
||||
|
||||
void Project::set_item_to_folder(QTreeWidgetItem* item) {
|
||||
item->setData(0, Qt::UserRole + 1, MEDIA_TYPE_FOLDER);
|
||||
}
|
||||
|
||||
Media* Project::get_media_from_tree(QTreeWidgetItem* item) {
|
||||
return reinterpret_cast<Media*>(item->data(0, Qt::UserRole + 1).value<quintptr>());
|
||||
return reinterpret_cast<Media*>(item->data(0, Qt::UserRole + 2).value<quintptr>());
|
||||
}
|
||||
|
||||
void Project::set_media_of_tree(QTreeWidgetItem* item, Media* media) {
|
||||
item->setData(0, Qt::UserRole + 1, QVariant::fromValue(reinterpret_cast<quintptr>(media)));
|
||||
item->setData(0, Qt::UserRole + 1, MEDIA_TYPE_FOOTAGE);
|
||||
item->setData(0, Qt::UserRole + 2, QVariant::fromValue(reinterpret_cast<quintptr>(media)));
|
||||
}
|
||||
|
||||
Sequence* Project::get_sequence_from_tree(QTreeWidgetItem* item) {
|
||||
return reinterpret_cast<Sequence*>(item->data(0, Qt::UserRole + 2).value<quintptr>());
|
||||
}
|
||||
|
||||
void Project::set_sequence_of_tree(QTreeWidgetItem* item, Sequence* sequence) {
|
||||
item->setData(0, Qt::UserRole + 1, MEDIA_TYPE_SEQUENCE);
|
||||
item->setData(0, Qt::UserRole + 2, QVariant::fromValue(reinterpret_cast<quintptr>(sequence)));
|
||||
}
|
||||
|
||||
int Project::get_type_from_tree(QTreeWidgetItem* item) {
|
||||
return item->data(0, Qt::UserRole + 1).toInt();
|
||||
}
|
||||
|
||||
void Project::delete_media(QTreeWidgetItem* item) {
|
||||
Media* m = get_media_from_tree(item);
|
||||
if (m->type == MEDIA_TYPE_SEQUENCE) {
|
||||
if (sequence == m->sequence) {
|
||||
int type = get_type_from_tree(item);
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
delete get_media_from_tree(item);
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
Sequence* s = get_sequence_from_tree(item);
|
||||
if (sequence == s) {
|
||||
set_sequence(NULL);
|
||||
}
|
||||
delete m->sequence;
|
||||
delete s;
|
||||
break;
|
||||
}
|
||||
delete m;
|
||||
|
||||
project_changed = true;
|
||||
}
|
||||
|
||||
@@ -573,8 +607,9 @@ void Project::save_project() {
|
||||
int len = ui->treeWidget->topLevelItemCount();
|
||||
stream.writeStartElement("media");
|
||||
for (int i=0;i<len;i++) {
|
||||
Media* m = get_media_from_tree(ui->treeWidget->topLevelItem(i));
|
||||
if (m->type == MEDIA_TYPE_FOOTAGE) {
|
||||
QTreeWidgetItem* item = ui->treeWidget->topLevelItem(i);
|
||||
if (get_type_from_tree(item) == MEDIA_TYPE_FOOTAGE) {
|
||||
Media* m = get_media_from_tree(item);
|
||||
m->save_id = i;
|
||||
stream.writeStartElement("footage");
|
||||
stream.writeAttribute("id", QString::number(i));
|
||||
@@ -587,9 +622,9 @@ void Project::save_project() {
|
||||
|
||||
stream.writeStartElement("timeline");
|
||||
for (int i=0;i<len;i++) {
|
||||
Media* m = get_media_from_tree(ui->treeWidget->topLevelItem(i));
|
||||
if (m->type == MEDIA_TYPE_SEQUENCE) {
|
||||
Sequence* s = m->sequence;
|
||||
QTreeWidgetItem* item = ui->treeWidget->topLevelItem(i);
|
||||
if (get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE) {
|
||||
Sequence* s = get_sequence_from_tree(item);
|
||||
stream.writeStartElement("sequence");
|
||||
stream.writeTextElement("name", s->name);
|
||||
stream.writeTextElement("width", QString::number(s->width));
|
||||
|
||||
@@ -11,6 +11,11 @@ class Viewer;
|
||||
class SourceTable;
|
||||
class QTreeWidgetItem;
|
||||
|
||||
#define MEDIA_TYPE_FOOTAGE 0
|
||||
#define MEDIA_TYPE_SEQUENCE 1
|
||||
#define MEDIA_TYPE_FOLDER 2
|
||||
#define MEDIA_TYPE_SOLID 3
|
||||
|
||||
namespace Ui {
|
||||
class Project;
|
||||
}
|
||||
@@ -40,12 +45,19 @@ public:
|
||||
void load_project();
|
||||
void save_project();
|
||||
|
||||
void new_folder();
|
||||
|
||||
int get_type_from_tree(QTreeWidgetItem* item);
|
||||
Media* get_media_from_tree(QTreeWidgetItem* item);
|
||||
void set_media_of_tree(QTreeWidgetItem* item, Media* media);
|
||||
Sequence* get_sequence_from_tree(QTreeWidgetItem* item);
|
||||
void set_sequence_of_tree(QTreeWidgetItem* item, Sequence* sequence);
|
||||
void set_item_to_folder(QTreeWidgetItem* item);
|
||||
|
||||
SourceTable* source_table;
|
||||
private:
|
||||
Ui::Project *ui;
|
||||
QTreeWidgetItem* new_item();
|
||||
private slots:
|
||||
void rename_media(QTreeWidgetItem* item, int column);
|
||||
};
|
||||
|
||||
+7
-1
@@ -35,6 +35,9 @@
|
||||
</property>
|
||||
<item>
|
||||
<widget class="SourceTable" name="treeWidget">
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
@@ -51,7 +54,10 @@
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="itemsExpandable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
|
||||
+17
-1
@@ -10,6 +10,7 @@
|
||||
#include <QtMath>
|
||||
#include <QDebug>
|
||||
|
||||
#define AUDIO_MONITOR_PEAK_HEIGHT 15
|
||||
#define AUDIO_MONITOR_GAP 3
|
||||
|
||||
extern "C" {
|
||||
@@ -39,24 +40,39 @@ void AudioMonitor::paintEvent(QPaintEvent *) {
|
||||
QPainter p(this);
|
||||
int channel_x = AUDIO_MONITOR_GAP;
|
||||
int channel_count = av_get_channel_layout_nb_channels(sequence->audio_layout);
|
||||
if (peaks.size() != channel_count) {
|
||||
peaks.resize(channel_count);
|
||||
peaks.fill(false);
|
||||
}
|
||||
int channel_width = (width()/channel_count) - AUDIO_MONITOR_GAP;
|
||||
long playhead_offset = -1;
|
||||
int i;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
QRect r(channel_x, 0, channel_width, height());
|
||||
QRect r(channel_x, AUDIO_MONITOR_PEAK_HEIGHT + AUDIO_MONITOR_GAP, channel_width, height());
|
||||
p.fillRect(r, gradient);
|
||||
|
||||
if (sample_cache_offset != -1) {
|
||||
playhead_offset = (panel_timeline->playhead - sample_cache_offset) * channel_count;
|
||||
if (playhead_offset >= 0 && playhead_offset < sample_cache.size()) {
|
||||
double multiplier = 1 - qAbs((double) sample_cache.at(playhead_offset+i) / 32768.0); // 16-bit int divided to float
|
||||
if (multiplier == (double) 0) {
|
||||
peaks[i] = true;
|
||||
}
|
||||
r.setHeight(r.height()*multiplier);
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
QRect peak_rect(channel_x, 0, channel_width, AUDIO_MONITOR_PEAK_HEIGHT);
|
||||
if (peaks.at(i)) {
|
||||
p.fillRect(peak_rect, QColor(255, 0, 0));
|
||||
} else {
|
||||
p.fillRect(peak_rect, QColor(64, 0, 0));
|
||||
}
|
||||
|
||||
p.fillRect(r, QColor(0, 0, 0, 160));
|
||||
|
||||
channel_x += channel_width + AUDIO_MONITOR_GAP;
|
||||
}
|
||||
if (playhead_offset > -1) {
|
||||
|
||||
@@ -22,6 +22,7 @@ public slots:
|
||||
|
||||
private:
|
||||
QLinearGradient gradient;
|
||||
QVector<bool> peaks;
|
||||
};
|
||||
|
||||
#endif // AUDIOMONITOR_H
|
||||
|
||||
+31
-9
@@ -13,14 +13,9 @@
|
||||
|
||||
SourceTable::SourceTable(QWidget* parent) : QTreeWidget(parent) {
|
||||
sortByColumn(0, Qt::AscendingOrder);
|
||||
setAcceptDrops(true);
|
||||
editing_item = NULL;
|
||||
rename_timer.setInterval(500);
|
||||
connect(&rename_timer, SIGNAL(timeout()), this, SLOT(rename_interval()));
|
||||
connect(this, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(item_click(QTreeWidgetItem*,int)));
|
||||
// connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(stop_rename_timer()));
|
||||
|
||||
// connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, )
|
||||
}
|
||||
|
||||
void SourceTable::stop_rename_timer() {
|
||||
@@ -50,9 +45,8 @@ void SourceTable::mouseDoubleClickEvent(QMouseEvent* ) {
|
||||
panel_project->import_dialog();
|
||||
} else if (selectedItems().count() == 1) {
|
||||
QTreeWidgetItem* item = selectedItems().at(0);
|
||||
Media* m = reinterpret_cast<Media*>(item->data(0, Qt::UserRole + 1).value<quintptr>());
|
||||
if (m->type == MEDIA_TYPE_SEQUENCE) {
|
||||
set_sequence(m->sequence);
|
||||
if (panel_project->get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE) {
|
||||
set_sequence(panel_project->get_sequence_from_tree(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +69,6 @@ void SourceTable::dragMoveEvent(QDragMoveEvent *event) {
|
||||
|
||||
void SourceTable::dropEvent(QDropEvent* event) {
|
||||
const QMimeData* mimeData = event->mimeData();
|
||||
|
||||
if (mimeData->hasUrls()) {
|
||||
QList<QUrl> urls = mimeData->urls();
|
||||
if (!urls.isEmpty()) {
|
||||
@@ -87,5 +80,34 @@ void SourceTable::dropEvent(QDropEvent* event) {
|
||||
panel_project->process_file_list(paths);
|
||||
}
|
||||
event->acceptProposedAction();
|
||||
} else {
|
||||
QTreeWidgetItem* item = itemAt(event->pos());
|
||||
if (item == NULL || (item != NULL && panel_project->get_type_from_tree(item) == MEDIA_TYPE_FOLDER)) {
|
||||
QList<QTreeWidgetItem*> items = selectedItems();
|
||||
for (int i=0;i<items.size();i++) {
|
||||
QTreeWidgetItem* s = items.at(i);
|
||||
bool ignore = false;
|
||||
if (s->parent() == NULL) {
|
||||
takeTopLevelItem(indexOfTopLevelItem(s));
|
||||
} else {
|
||||
// if child belongs to a selected parent, assume the user is just moving the parent and ignore the child
|
||||
for (int j=0;j<items.size();j++) {
|
||||
if (s->parent() == items.at(j)) {
|
||||
ignore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ignore) s->parent()->takeChild(s->parent()->indexOfChild(s));
|
||||
}
|
||||
if (!ignore) {
|
||||
if (item == NULL) {
|
||||
addTopLevelItem(s);
|
||||
} else {
|
||||
item->addChild(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
project_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-21
@@ -59,27 +59,29 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
panel_timeline->drag_frame_start = entry_point + panel_timeline->getFrameFromScreenPoint(50);
|
||||
panel_timeline->drag_track_start = (bottom_align) ? -1 : 0;
|
||||
for (int i=0;i<items.size();i++) {
|
||||
bool ignore_infinite_length = false;
|
||||
Media* m = panel_project->get_media_from_tree(items.at(i));
|
||||
Ghost g;
|
||||
g.clip = -1;
|
||||
g.media = m;
|
||||
g.old_clip_in = g.clip_in = 0;
|
||||
g.in = entry_point;
|
||||
entry_point += m->get_length_in_frames(sequence->frame_rate);
|
||||
g.out = entry_point;
|
||||
g.trimming = false;
|
||||
for (int j=0;j<m->audio_tracks.size();j++) {
|
||||
g.track = j;
|
||||
g.media_stream = m->audio_tracks.at(j);
|
||||
ignore_infinite_length = true;
|
||||
panel_timeline->ghosts.append(g);
|
||||
}
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
g.track = -1-j;
|
||||
g.media_stream = m->video_tracks.at(j);
|
||||
if (m->video_tracks[j]->infinite_length && !ignore_infinite_length) g.out = g.in + 100;
|
||||
panel_timeline->ghosts.append(g);
|
||||
QTreeWidgetItem* item = items.at(i);
|
||||
if (panel_project->get_type_from_tree(item) == MEDIA_TYPE_FOOTAGE) {
|
||||
Media* m = panel_project->get_media_from_tree(item);
|
||||
bool ignore_infinite_length = (m->audio_tracks.size() > 0);
|
||||
Ghost g;
|
||||
g.clip = -1;
|
||||
g.media = m;
|
||||
g.old_clip_in = g.clip_in = 0;
|
||||
g.in = entry_point;
|
||||
entry_point += m->get_length_in_frames(sequence->frame_rate);
|
||||
g.out = entry_point;
|
||||
g.trimming = false;
|
||||
for (int j=0;j<m->audio_tracks.size();j++) {
|
||||
g.track = j;
|
||||
g.media_stream = m->audio_tracks.at(j);
|
||||
panel_timeline->ghosts.append(g);
|
||||
}
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
g.track = -1-j;
|
||||
g.media_stream = m->video_tracks.at(j);
|
||||
if (m->video_tracks[j]->infinite_length && !ignore_infinite_length) g.out = g.in + 100;
|
||||
panel_timeline->ghosts.append(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
|
||||
Reference in New Issue
Block a user