diff --git a/icons/icons.qrc b/icons/icons.qrc index facafcbdd..3fc0faaf0 100644 --- a/icons/icons.qrc +++ b/icons/icons.qrc @@ -29,5 +29,6 @@ slip-disabled.png slide.png slide-disabled.png + throbber.png diff --git a/icons/throbber.png b/icons/throbber.png new file mode 100644 index 000000000..208c0aecc Binary files /dev/null and b/icons/throbber.png differ diff --git a/io/previewgenerator.cpp b/io/previewgenerator.cpp index 5b808e8aa..2804adced 100644 --- a/io/previewgenerator.cpp +++ b/io/previewgenerator.cpp @@ -1,7 +1,3 @@ -// -// TODO: Fold this file into a multithreaded "file processor" -// - #include "previewgenerator.h" #include "media.h" @@ -10,6 +6,9 @@ #include #include #include +#include + +#define WAVEFORM_RESOLUTION 64 extern "C" { #include @@ -18,16 +17,46 @@ extern "C" { #include } -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; } diff --git a/io/previewgenerator.h b/io/previewgenerator.h index 35ffc3b5d..293c57614 100644 --- a/io/previewgenerator.h +++ b/io/previewgenerator.h @@ -3,15 +3,27 @@ #include +#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; }; diff --git a/mainwindow.cpp b/mainwindow.cpp index 76bdbe5be..33f303884 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #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)); diff --git a/panels/project.cpp b/panels/project.cpp index 6b0705edb..28fe01996 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -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(); +} diff --git a/panels/project.h b/panels/project.h index 773f4d23b..07525755c 100644 --- a/panels/project.h +++ b/panels/project.h @@ -3,6 +3,7 @@ #include #include +#include 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 diff --git a/project/effect.cpp b/project/effect.cpp index bec57d341..dd8575f8e 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -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) {}