implemented multithreaded importing #85

This commit is contained in:
itsmattkc
2018-07-18 21:07:16 +01:00
parent 5e70fd4534
commit 735a53720b
8 changed files with 146 additions and 82 deletions
+1
View File
@@ -29,5 +29,6 @@
<file>slip-disabled.png</file>
<file>slide.png</file>
<file>slide-disabled.png</file>
<file>throbber.png</file>
</qresource>
</RCC>
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+76 -12
View File
@@ -1,7 +1,3 @@
//
// TODO: Fold this file into a multithreaded "file processor"
//
#include "previewgenerator.h"
#include "media.h"
@@ -10,6 +6,9 @@
#include <QPixmap>
#include <QDebug>
#include <QtMath>
#include <QTreeWidgetItem>
#define WAVEFORM_RESOLUTION 64
extern "C" {
#include <libavformat/avformat.h>
@@ -18,16 +17,46 @@ extern "C" {
#include <libswresample/swresample.h>
}
PreviewGenerator::PreviewGenerator(QObject* parent) : QThread(parent) {
media = NULL;
fmt_ctx = NULL;
PreviewGenerator::PreviewGenerator(QTreeWidgetItem* i, Media* m) : QThread(0), fmt_ctx(NULL), item(i), media(m) {
connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
}
#define WAVEFORM_RESOLUTION 64
void PreviewGenerator::parse_media() {
// detect video/audio streams in file
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) {
qDebug() << "[ERROR] Unsupported codec in stream %d.\n";
} else {
MediaStream* ms = new MediaStream();
ms->preview_done = false;
ms->file_index = i;
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
bool infinite_length = (fmt_ctx->streams[i]->avg_frame_rate.den == 0);
ms->video_width = fmt_ctx->streams[i]->codecpar->width;
ms->video_height = fmt_ctx->streams[i]->codecpar->height;
ms->infinite_length = infinite_length;
media->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;
media->audio_tracks.append(ms);
}
}
}
media->length = fmt_ctx->duration;
void PreviewGenerator::run() {
Q_ASSERT(media != NULL);
media->ready = true;
if (media->video_tracks.size() == 0) {
emit set_icon(ICON_TYPE_AUDIO);
} else {
emit set_icon(ICON_TYPE_VIDEO);
}
item->setText(1, QString::number(media->length));
}
void PreviewGenerator::generate_waveform() {
SwsContext* sws_ctx;
SwrContext* swr_ctx;
AVFrame* temp_frame = av_frame_alloc();
@@ -99,7 +128,7 @@ void PreviewGenerator::run() {
s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGB888);
// delete [] data;
// delete [] data;
s->preview_done = true;
@@ -183,6 +212,41 @@ void PreviewGenerator::run() {
}
}
delete [] codec_ctx;
}
avformat_close_input(&fmt_ctx);
void PreviewGenerator::run() {
Q_ASSERT(media != NULL);
Q_ASSERT(item != NULL);
QByteArray ba = media->url.toLatin1();
char* filename = new char[ba.size()+1];
strcpy(filename, ba.data());
bool error = false;
int errCode = avformat_open_input(&fmt_ctx, filename, NULL, NULL);
if(errCode != 0) {
char err[1024];
av_strerror(errCode, err, 1024);
item->setToolTip(0, "Could not open file - " + QString(err));
error = true;
} else {
errCode = avformat_find_stream_info(fmt_ctx, NULL);
if (errCode < 0) {
char err[1024];
av_strerror(errCode, err, 1024);
item->setToolTip(0, "Could not find stream information - " + QString(err));
error = true;
} else {
av_dump_format(fmt_ctx, 0, filename, 0);
parse_media();
generate_waveform();
}
avformat_close_input(&fmt_ctx);
}
if (error) {
emit set_icon(ICON_TYPE_ERROR);
} else {
item->setToolTip(0, QString());
}
delete [] filename;
}
+13 -1
View File
@@ -3,15 +3,27 @@
#include <QThread>
#define ICON_TYPE_VIDEO 0
#define ICON_TYPE_AUDIO 1
#define ICON_TYPE_ERROR 2
struct Media;
struct MediaStream;
struct AVFormatContext;
class QTreeWidgetItem;
class PreviewGenerator : public QThread
{
Q_OBJECT
public:
PreviewGenerator(QObject* parent = 0);
PreviewGenerator(QTreeWidgetItem*, Media*);
void run();
signals:
void set_icon(int);
private:
void parse_media();
void generate_waveform();
QTreeWidgetItem* item;
Media* media;
AVFormatContext* fmt_ctx;
};
+2 -1
View File
@@ -27,6 +27,7 @@
#include <QStandardPaths>
#include <QTimer>
#include <QCloseEvent>
#include <QMovie>
#define OLIVE_FILE_FILTER "Olive Project (*.ove)"
@@ -58,7 +59,7 @@ MainWindow::MainWindow(QWidget *parent) :
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, QColor(25,25,25));
darkPalette.setColor(QPalette::AlternateBase, QColor(53,53,53));
darkPalette.setColor(QPalette::ToolTipBase, Qt::white);
darkPalette.setColor(QPalette::ToolTipBase, QColor(25,25,25));
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, QColor(53,53,53));
+38 -62
View File
@@ -111,77 +111,22 @@ void Project::new_sequence(Sequence *s, bool open, QTreeWidgetItem* parent) {
}
QTreeWidgetItem* Project::import_file(QString file) {
QByteArray ba = file.toLatin1();
char* filename = new char[ba.size()+1];
strcpy(filename, ba.data());
QTreeWidgetItem* item = new_item();
Media* m = new Media();
m->url = file;
m->name = file.mid(file.lastIndexOf('/')+1);
item->setText(0, m->name);
AVFormatContext* pFormatCtx = NULL;
int errCode = avformat_open_input(&pFormatCtx, filename, NULL, NULL);
if(errCode != 0) {
char err[1024];
av_strerror(errCode, err, 1024);
qDebug() << "[ERROR] Could not open" << filename << "-" << err;
} else {
errCode = avformat_find_stream_info(pFormatCtx, NULL);
if (errCode < 0) {
char err[1024];
av_strerror(errCode, err, 1024);
fprintf(stderr, "[ERROR] Could not find stream information. %s\n", err);
} else {
av_dump_format(pFormatCtx, 0, filename, 0);
// detect video/audio streams in file
for (int i=0;i<(int)pFormatCtx->nb_streams;i++) {
// Find the decoder for the video stream
if (avcodec_find_decoder(pFormatCtx->streams[i]->codecpar->codec_id) == NULL) {
qDebug() << "[ERROR] Unsupported codec in stream %d.\n";
} else {
MediaStream* ms = new MediaStream();
ms->preview_done = false;
ms->file_index = i;
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
bool infinite_length = (pFormatCtx->streams[i]->avg_frame_rate.den == 0);
ms->video_width = pFormatCtx->streams[i]->codecpar->width;
ms->video_height = pFormatCtx->streams[i]->codecpar->height;
ms->infinite_length = infinite_length;
m->video_tracks.append(ms);
} else if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
ms->audio_channels = pFormatCtx->streams[i]->codecpar->channels;
m->audio_tracks.append(ms);
}
}
}
m->length = pFormatCtx->duration;
if (m->video_tracks.size() == 0) {
item->setIcon(0, QIcon(":/icons/audiosource.png"));
} else {
item->setIcon(0, QIcon(":/icons/videosource.png"));
}
item->setText(1, QString::number(m->length));
m->ready = 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);
}
}
set_media_of_tree(item, m);
project_changed = true;
// set up throbber animation
MediaThrobber* throbber = new MediaThrobber(item);
// generate waveform/thumbnail in another thread
PreviewGenerator* pg = new PreviewGenerator(item, m);
connect(pg, SIGNAL(set_icon(int)), throbber, SLOT(stop(int)));
pg->start(QThread::LowPriority);
delete [] filename;
return item;
}
@@ -377,6 +322,7 @@ void Project::process_file_list(QStringList& files) {
ta->add_media(import_file(file));
imported = true;
project_changed = true;
}
if (imported) {
undo_stack.push(ta);
@@ -935,3 +881,33 @@ void Project::add_recent_project(QString url) {
}
save_recent_projects();
}
#define THROBBER_LIMIT 20
#define THROBBER_SIZE 50
MediaThrobber::MediaThrobber(QTreeWidgetItem* i) : pixmap(":/icons/throbber.png"), item(i), animation(0) {
// set up throbber
animation_update();
animator.setInterval(20);
connect(&animator, SIGNAL(timeout()), this, SLOT(animation_update()));
animator.start();
}
void MediaThrobber::animation_update() {
if (animation == THROBBER_LIMIT) {
animation = 0;
}
item->setIcon(0, QIcon(pixmap.copy(THROBBER_SIZE*animation, 0, THROBBER_SIZE, THROBBER_SIZE)));
animation++;
}
void MediaThrobber::stop(int icon_type) {
animator.stop();
switch (icon_type) {
case ICON_TYPE_VIDEO: item->setIcon(0, QIcon(":/icons/videosource.png")); break;
case ICON_TYPE_AUDIO: item->setIcon(0, QIcon(":/icons/audiosource.png")); break;
case ICON_TYPE_ERROR: item->setIcon(0, QIcon::fromTheme("dialog-error")); break;
}
panel_project->source_table->viewport()->update();
deleteLater();
}
+16
View File
@@ -3,6 +3,7 @@
#include <QDockWidget>
#include <QVector>
#include <QTimer>
struct Media;
struct Sequence;
@@ -84,4 +85,19 @@ private slots:
void clear_recent_projects();
};
class MediaThrobber : public QObject {
Q_OBJECT
public:
MediaThrobber(QTreeWidgetItem*);
public slots:
void stop(int);
private slots:
void animation_update();
private:
QPixmap pixmap;
int animation;
QTreeWidgetItem* item;
QTimer animator;
};
#endif // PROJECT_H
-6
View File
@@ -35,11 +35,5 @@ bool Effect::is_enabled() {
Effect* Effect::copy(Clip*) {return NULL;}
void Effect::load(QXmlStreamReader*) {}
void Effect::save(QXmlStreamWriter*) {}
/*void Effect::import_values(float* val, int count) {
qDebug() << "[ERROR] import_values MUST be overridden";
}
void Effect::export_values(float* val, int* count) {
qDebug() << "[ERROR] export_values MUST be overridden";
}*/
void Effect::process_gl(int*, int*) {}
void Effect::process_audio(uint8_t*, int) {}