diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index d558f1a12..ab2a37657 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,7 @@ void PreferencesDialog::save() { config.recording_mode = recordingComboBox->currentIndex() + 1; config.img_seq_formats = imgSeqFormatEdit->text(); config.fast_seeking = fastSeekButton->isChecked(); + config.disable_multithreading_for_images = disable_img_multithread->isChecked(); // save keyboard shortcuts for (int i=0;iaddTab(tab, "General"); QWidget* tab_2 = new QWidget(); tabWidget->addTab(tab_2, "Behavior"); - QWidget* tab_4 = new QWidget(); - QVBoxLayout* verticalLayout_2 = new QVBoxLayout(tab_4); - QGroupBox* groupBox = new QGroupBox(tab_4); - groupBox->setTitle("Seeking"); - QVBoxLayout* verticalLayout_3 = new QVBoxLayout(groupBox); - accurateSeekButton = new QRadioButton(groupBox); + + // Playback + QWidget* playback_tab = new QWidget(); + QVBoxLayout* playback_tab_layout = new QVBoxLayout(playback_tab); + + // Playback -> Seeking + QGroupBox* seeking_group = new QGroupBox(playback_tab); + seeking_group->setTitle("Seeking"); + QVBoxLayout* seeking_group_layout = new QVBoxLayout(seeking_group); + accurateSeekButton = new QRadioButton(seeking_group); accurateSeekButton->setText("Accurate Seeking\nAlways show the correct frame (visual may pause briefly as correct frame is retrieved)"); - - verticalLayout_3->addWidget(accurateSeekButton); - - fastSeekButton = new QRadioButton(groupBox); + seeking_group_layout->addWidget(accurateSeekButton); + fastSeekButton = new QRadioButton(seeking_group); fastSeekButton->setText("Fast Seeking\nSeek quickly (may briefly show inaccurate frames when seeking - doesn't affect playback/export)"); + seeking_group_layout->addWidget(fastSeekButton); + playback_tab_layout->addWidget(seeking_group); - verticalLayout_3->addWidget(fastSeekButton); + // Playback -> Disable Multithreading on Images + disable_img_multithread = new QCheckBox("Disable Multithreading on Images"); + disable_img_multithread->setChecked(config.disable_multithreading_for_images); + playback_tab_layout->addWidget(disable_img_multithread); - verticalLayout_2->addWidget(groupBox); - - tabWidget->addTab(tab_4, "Playback"); + tabWidget->addTab(playback_tab, "Playback"); QWidget* shortcut_tab = new QWidget(); diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index 9e3031bc5..4db3f5037 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -10,6 +10,7 @@ class QRadioButton; class QTreeWidget; class QTreeWidgetItem; class QMenu; +class QCheckBox; class KeySequenceEditor : public QKeySequenceEdit { Q_OBJECT @@ -44,6 +45,7 @@ private: QRadioButton* accurateSeekButton; QRadioButton* fastSeekButton; QTreeWidget* keyboard_tree; + QCheckBox* disable_img_multithread; QVector key_shortcut_actions; QVector key_shortcut_items; diff --git a/io/config.cpp b/io/config.cpp index e36e7b763..00e902719 100644 --- a/io/config.cpp +++ b/io/config.cpp @@ -38,7 +38,8 @@ Config::Config() hover_focus(false), project_view_type(PROJECT_VIEW_TREE), set_name_with_marker(true), - show_project_toolbar(false) + show_project_toolbar(false), + disable_multithreading_for_images(false) {} void Config::load(QString path) { @@ -130,6 +131,9 @@ void Config::load(QString path) { } else if (stream.name() == "ShowProjectToolbar") { stream.readNext(); show_project_toolbar = (stream.text() == "1"); + } else if (stream.name() == "DisableMultithreadedImages") { + stream.readNext(); + disable_multithreading_for_images = (stream.text() == "1"); } } } @@ -181,6 +185,7 @@ void Config::save(QString path) { stream.writeTextElement("ProjectViewType", QString::number(project_view_type)); stream.writeTextElement("SetNameWithMarker", QString::number(set_name_with_marker)); stream.writeTextElement("ShowProjectToolbar", QString::number(panel_project->toolbar_widget->isVisible())); + stream.writeTextElement("DisableMultithreadedImages", QString::number(disable_multithreading_for_images)); stream.writeEndElement(); // configuration stream.writeEndDocument(); // doc diff --git a/io/config.h b/io/config.h index 312ba5c84..25245e89f 100644 --- a/io/config.h +++ b/io/config.h @@ -50,6 +50,7 @@ struct Config { int project_view_type; bool set_name_with_marker; bool show_project_toolbar; + bool disable_multithreading_for_images; void load(QString path); void save(QString path); diff --git a/io/previewgenerator.cpp b/io/previewgenerator.cpp index afb753ef4..6c27e3932 100644 --- a/io/previewgenerator.cpp +++ b/io/previewgenerator.cpp @@ -32,14 +32,14 @@ QSemaphore sem(5); // only 5 preview generators can run at one time PreviewGenerator::PreviewGenerator(Media* i, Footage* m, bool r) : QThread(0), fmt_ctx(NULL), - media(i), - footage(m), + media(i), + footage(m), retrieve_duration(false), contains_still_image(false), replace(r), cancelled(false) { - data_path = get_data_path() + "/previews"; + data_path = get_data_path() + "/previews"; QDir data_dir(data_path); if (!data_dir.exists()) { data_dir.mkpath("."); @@ -49,72 +49,79 @@ PreviewGenerator::PreviewGenerator(Media* i, Footage* m, bool r) : } 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) { - dout << "[ERROR] Unsupported codec in stream" << i << "of file" << footage->name; - } else { - FootageStream ms; - ms.preview_done = false; - ms.file_index = i; - ms.enabled = true; + // 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) { + dout << "[ERROR] Unsupported codec in stream" << i << "of file" << footage->name; + } else { + FootageStream ms; + ms.preview_done = false; + ms.file_index = i; + ms.enabled = true; - bool append = false; + bool append = false; if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && fmt_ctx->streams[i]->codecpar->width > 0 && fmt_ctx->streams[i]->codecpar->height > 0) { - /*dout << "avg_frame_rate was:" << fmt_ctx->streams[i]->avg_frame_rate.num << "/" << fmt_ctx->streams[i]->avg_frame_rate.den; + /*dout << "avg_frame_rate was:" << fmt_ctx->streams[i]->avg_frame_rate.num << "/" << fmt_ctx->streams[i]->avg_frame_rate.den; dout << "r_frame_rate was:" << fmt_ctx->streams[i]->r_frame_rate.num << "/" << fmt_ctx->streams[i]->r_frame_rate.den; dout << "codec_frame_rate was:" << fmt_ctx->streams[i]->codec->framerate.num << "/" << fmt_ctx->streams[i]->codec->framerate.den; dout << "nb_frames was:" << fmt_ctx->streams[i]->nb_frames; - dout << "duration was:" << fmt_ctx->streams[i]->duration << "OR fmt_ctx's duration is:" << fmt_ctx->duration;*/ + dout << "duration was:" << fmt_ctx->streams[i]->duration << "OR fmt_ctx's duration is:" << fmt_ctx->duration;*/ - // heuristic to determine if video is a still image + // heuristic to determine if video is a still image if (fmt_ctx->streams[i]->avg_frame_rate.den == 0 - && fmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_DNXHD) { // silly hack but this is the only scenario i've seen this - ms.infinite_length = true; - contains_still_image = true; - ms.video_frame_rate = 0; - } else { - ms.infinite_length = false; - if (fmt_ctx->streams[i]->r_frame_rate.den == 0) { - ms.video_frame_rate = av_q2d(fmt_ctx->streams[i]->avg_frame_rate); + && fmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_DNXHD) { // silly hack but this is the only scenario i've seen this + if (footage->url.contains('%')) { + // must be an image sequence + ms.infinite_length = false; + ms.video_frame_rate = 25; } else { - ms.video_frame_rate = av_q2d(fmt_ctx->streams[i]->r_frame_rate); + ms.infinite_length = true; + contains_still_image = true; + ms.video_frame_rate = 0; + } + } else { + ms.infinite_length = false; + if (fmt_ctx->streams[i]->r_frame_rate.den == 0) { + ms.video_frame_rate = av_q2d(fmt_ctx->streams[i]->avg_frame_rate); + } else { + ms.video_frame_rate = av_q2d(fmt_ctx->streams[i]->r_frame_rate); } } - ms.video_width = fmt_ctx->streams[i]->codecpar->width; - ms.video_height = fmt_ctx->streams[i]->codecpar->height; + + ms.video_width = fmt_ctx->streams[i]->codecpar->width; + ms.video_height = fmt_ctx->streams[i]->codecpar->height; // default value, we get the true value later in generate_waveform() - ms.video_auto_interlacing = VIDEO_PROGRESSIVE; - ms.video_interlacing = VIDEO_PROGRESSIVE; + ms.video_auto_interlacing = VIDEO_PROGRESSIVE; + ms.video_interlacing = VIDEO_PROGRESSIVE; - append = true; - } 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; + append = true; + } 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; - append = true; - } + append = true; + } - if (append) { - QVector& stream_list = (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ? footage->audio_tracks : footage->video_tracks; - for (int j=0;jlength = fmt_ctx->duration; + if (append) { + QVector& stream_list = (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ? footage->audio_tracks : footage->video_tracks; + for (int j=0;jlength = fmt_ctx->duration; if (fmt_ctx->duration == INT64_MIN) { retrieve_duration = true; @@ -126,38 +133,38 @@ 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"; + //dout << "[NOTE] " << media->name << "needs to retrieve duration"; return true; } bool found = true; - for (int i=0;ivideo_tracks.size();i++) { - FootageStream& ms = footage->video_tracks[i]; + for (int i=0;ivideo_tracks.size();i++) { + FootageStream& ms = footage->video_tracks[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; - ms.make_square_thumb(); - ms.preview_done = true; + if (f.exists() && ms.video_preview.load(thumb_path)) { + //dout << "loaded thumb" << ms->file_index << "from" << thumb_path; + ms.make_square_thumb(); + ms.preview_done = true; } else { found = false; break; } } - for (int i=0;iaudio_tracks.size();i++) { - FootageStream& ms = footage->audio_tracks[i]; + for (int i=0;iaudio_tracks.size();i++) { + FootageStream& ms = footage->audio_tracks[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()); + ms.audio_preview.resize(data.size()); for (int j=0;jvideo_tracks.size();i++) { - FootageStream& ms = footage->video_tracks[i]; - ms.preview_done = false; + for (int i=0;ivideo_tracks.size();i++) { + FootageStream& ms = footage->video_tracks[i]; + ms.preview_done = false; } - for (int i=0;iaudio_tracks.size();i++) { - FootageStream& ms = footage->audio_tracks[i]; - ms.audio_preview.clear(); - ms.preview_done = false; + for (int i=0;iaudio_tracks.size();i++) { + FootageStream& ms = footage->audio_tracks[i]; + ms.audio_preview.clear(); + ms.preview_done = false; } } return !found; } void PreviewGenerator::finalize_media() { - footage->ready_lock.unlock(); - footage->ready = true; + footage->ready_lock.unlock(); + footage->ready = true; - if (!cancelled) { - 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); - } else { - emit set_icon(ICON_TYPE_VIDEO, replace); - } + if (!cancelled) { + 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); + } else { + emit set_icon(ICON_TYPE_VIDEO, replace); + } - /*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)); + /*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)); - if (media->video_tracks.size() > 0) { - item->setText(2, QString::number(frame_rate) + " FPS"); - } else { - item->setText(2, QString::number(media->audio_tracks.at(0)->audio_frequency) + " Hz"); - } - }*/ - } + if (media->video_tracks.size() > 0) { + item->setText(2, QString::number(frame_rate) + " FPS"); + } else { + item->setText(2, QString::number(media->audio_tracks.at(0)->audio_frequency) + " Hz"); + } + }*/ + } } void PreviewGenerator::generate_waveform() { - SwsContext* sws_ctx; - SwrContext* swr_ctx; - AVFrame* temp_frame = av_frame_alloc(); - AVCodecContext** codec_ctx = new AVCodecContext* [fmt_ctx->nb_streams]; + SwsContext* sws_ctx; + SwrContext* swr_ctx; + AVFrame* temp_frame = av_frame_alloc(); + AVCodecContext** codec_ctx = new AVCodecContext* [fmt_ctx->nb_streams]; int64_t* media_lengths = new int64_t[fmt_ctx->nb_streams]{0}; - for (unsigned int i=0;inb_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); + for (unsigned int i=0;inb_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); if (codec != NULL) { codec_ctx[i] = avcodec_alloc_context3(codec); avcodec_parameters_to_context(codec_ctx[i], fmt_ctx->streams[i]->codecpar); @@ -223,13 +230,13 @@ void PreviewGenerator::generate_waveform() { codec_ctx[i]->channel_layout = av_get_default_channel_layout(fmt_ctx->streams[i]->codecpar->channels); } } - } + } } AVPacket* packet = av_packet_alloc(); - bool done = true; + bool done = true; - bool end_of_file = false; + bool end_of_file = false; // get the ball rolling do { @@ -242,30 +249,30 @@ void PreviewGenerator::generate_waveform() { 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; + //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 < 0) { + end_of_file = true; if (read_ret != AVERROR_EOF) dout << "[ERROR] Failed to read packet for preview generation" << read_ret; - break; - } + break; + } if (codec_ctx[packet->stream_index] != NULL) { int send_ret = avcodec_send_packet(codec_ctx[packet->stream_index], packet); - if (send_ret < 0 && send_ret != AVERROR(EAGAIN)) { + if (send_ret < 0 && send_ret != AVERROR(EAGAIN)) { dout << "[ERROR] Failed to send packet for preview generation - aborting" << send_ret; - end_of_file = true; - break; - } - } + end_of_file = true; + break; + } + } } - if (!end_of_file) { - FootageStream* s = footage->get_stream_from_file_index(fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, packet->stream_index); + if (!end_of_file) { + 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) { int dstH = 120; int dstW = dstH * ((float)temp_frame->width/(float)temp_frame->height); - uint8_t* data = new uint8_t[dstW*dstH*4]; + uint8_t* data = new uint8_t[dstW*dstH*4]; sws_ctx = sws_getContext( temp_frame->width, @@ -273,7 +280,7 @@ void PreviewGenerator::generate_waveform() { static_cast(temp_frame->format), dstW, dstH, - static_cast(AV_PIX_FMT_RGBA), + static_cast(AV_PIX_FMT_RGBA), SWS_FAST_BILINEAR, NULL, NULL, @@ -281,11 +288,11 @@ void PreviewGenerator::generate_waveform() { ); int linesize[AV_NUM_DATA_POINTERS]; - linesize[0] = dstW*4; + linesize[0] = dstW*4; 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_RGBA8888); - s->make_square_thumb(); + s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGBA8888); + s->make_square_thumb(); // is video interlaced? s->video_auto_interlacing = (temp_frame->interlaced_frame) ? ((temp_frame->top_field_first) ? VIDEO_TOP_FIELD_FIRST : VIDEO_BOTTOM_FIELD_FIRST) : VIDEO_PROGRESSIVE; @@ -345,7 +352,7 @@ void PreviewGenerator::generate_waveform() { break; } } - s->audio_preview.append(min >> 8); + s->audio_preview.append(min >> 8); s->audio_preview.append(max >> 8); if (cancelled) break; } @@ -365,10 +372,10 @@ void PreviewGenerator::generate_waveform() { // check if we've got all our previews if (retrieve_duration) { done = false; - } else if (footage->audio_tracks.size() == 0) { + } else if (footage->audio_tracks.size() == 0) { done = true; - for (int i=0;ivideo_tracks.size();i++) { - if (!footage->video_tracks.at(i).preview_done) { + for (int i=0;ivideo_tracks.size();i++) { + if (!footage->video_tracks.at(i).preview_done) { done = false; break; } @@ -379,27 +386,27 @@ void PreviewGenerator::generate_waveform() { } } av_packet_unref(packet); - } - } - for (int i=0;iaudio_tracks.size();i++) { - footage->audio_tracks[i].preview_done = true; - } - av_frame_free(&temp_frame); + } + } + for (int i=0;iaudio_tracks.size();i++) { + footage->audio_tracks[i].preview_done = true; + } + av_frame_free(&temp_frame); av_packet_free(&packet); - for (unsigned int i=0;inb_streams;i++) { - if (codec_ctx[i] != NULL) { - avcodec_close(codec_ctx[i]); - } + for (unsigned int i=0;inb_streams;i++) { + if (codec_ctx[i] != NULL) { + avcodec_close(codec_ctx[i]); + } } if (retrieve_duration) { - footage->length = 0; + footage->length = 0; int maximum_stream = 0; for (unsigned int i=0;inb_streams;i++) { if (media_lengths[i] > media_lengths[maximum_stream]) { maximum_stream = i; } } - footage->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; @@ -407,83 +414,83 @@ void PreviewGenerator::generate_waveform() { } QString PreviewGenerator::get_thumbnail_path(const QString& hash, const FootageStream& ms) { - return data_path + "/" + hash + "t" + QString::number(ms.file_index); + return data_path + "/" + hash + "t" + QString::number(ms.file_index); } QString PreviewGenerator::get_waveform_path(const QString& hash, const FootageStream& ms) { - return data_path + "/" + hash + "w" + QString::number(ms.file_index); + return data_path + "/" + hash + "w" + QString::number(ms.file_index); } -void PreviewGenerator::run() { - Q_ASSERT(footage != NULL); - Q_ASSERT(media != NULL); +void PreviewGenerator::run() { + Q_ASSERT(footage != NULL); + Q_ASSERT(media != NULL); QByteArray ba = footage->url.toUtf8(); - char* filename = new char[ba.size()+1]; + char* filename = new char[ba.size()+1]; strcpy(filename, ba.data()); QString errorStr; - bool error = false; - int errCode = avformat_open_input(&fmt_ctx, filename, NULL, NULL); - if(errCode != 0) { - char err[1024]; - av_strerror(errCode, err, 1024); + bool error = false; + int errCode = avformat_open_input(&fmt_ctx, filename, NULL, NULL); + if(errCode != 0) { + char err[1024]; + av_strerror(errCode, err, 1024); errorStr = "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); + error = true; + } else { + errCode = avformat_find_stream_info(fmt_ctx, NULL); + if (errCode < 0) { + char err[1024]; + av_strerror(errCode, err, 1024); errorStr = "Could not find stream information - " + QString(err); - error = true; - } else { - av_dump_format(fmt_ctx, 0, filename, 0); - parse_media(); + error = true; + } else { + av_dump_format(fmt_ctx, 0, filename, 0); + parse_media(); // see if we already have data for this - 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; + 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.toUtf8(), QCryptographicHash::Md5).toHex(); if (retrieve_preview(hash)) { - sem.acquire(); + sem.acquire(); generate_waveform(); // save preview to file - for (int i=0;ivideo_tracks.size();i++) { - FootageStream& ms = footage->video_tracks[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;ivideo_tracks.size();i++) { + FootageStream& ms = footage->video_tracks[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;iaudio_tracks.size();i++) { - FootageStream& ms = footage->audio_tracks[i]; + for (int i=0;iaudio_tracks.size();i++) { + FootageStream& ms = footage->audio_tracks[i]; QFile f(get_waveform_path(hash, ms)); f.open(QFile::WriteOnly); - f.write(ms.audio_preview.constData(), ms.audio_preview.size()); + 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); + } + avformat_close_input(&fmt_ctx); } - if (error) { - media->update_tooltip(errorStr); + if (error) { + media->update_tooltip(errorStr); emit set_icon(ICON_TYPE_ERROR, replace); - footage->invalid = true; - footage->ready_lock.unlock(); + footage->invalid = true; + footage->ready_lock.unlock(); } else { - media->update_tooltip(); - } + media->update_tooltip(); + } - delete [] filename; - footage->preview_gen = NULL; + delete [] filename; + footage->preview_gen = NULL; } void PreviewGenerator::cancel() { diff --git a/playback/cacher.cpp b/playback/cacher.cpp index 7df2ad948..2450b6943 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -13,6 +13,7 @@ #include "panels/panels.h" #include "panels/viewer.h" #include "project/media.h" +#include "io/config.h" #include "debug.h" extern "C" { @@ -458,7 +459,9 @@ void cache_video_worker(Clip* c, long playhead) { if (c->multithreaded && c->cacher->interrupt) return; // abort AVFrame* send_frame = c->frame; +// qint64 time = QDateTime::currentMSecsSinceEpoch(); read_ret = (c->use_existing_frame) ? 0 : retrieve_next_frame(c, send_frame); +// dout << QDateTime::currentMSecsSinceEpoch() - time; c->use_existing_frame = false; if (read_ret >= 0) { bool send_it = true; @@ -677,10 +680,11 @@ void open_clip_worker(Clip* clip) { clip->opts = NULL; // 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) { + 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) + || !config.disable_multithreading_for_images) { av_dict_set(&clip->opts, "threads", "auto", 0); } if (clip->stream->codecpar->codec_id == AV_CODEC_ID_H264) { diff --git a/playback/playback.cpp b/playback/playback.cpp index cd6c906d7..9cc1daf7b 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -48,7 +48,7 @@ void open_clip(Clip* clip, bool multithreaded) { // 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); + clip->cacher->start((clip->track < 0) ? QThread::HighPriority : QThread::TimeCriticalPriority); } } else { clip->finished_opening = false; @@ -83,7 +83,10 @@ void close_clip(Clip* clip, bool wait) { if (clip->multithreaded) { clip->cacher->caching = false; clip->can_cache.wakeAll(); - if (wait) clip->cacher->wait(); + if (wait) { + clip->open_lock.lock(); + clip->open_lock.unlock(); + } } else { close_clip_worker(clip); } @@ -172,7 +175,7 @@ void get_clip_frame(Clip* c, long playhead) { } if (c->queue.at(i) != target_frame && ((c->queue.at(i)->pts > minimum_ts) == c->reverse)) { //dout << "removed frame at" << i << "because its pts was" << c->queue.at(i)->pts << "compared to" << target_frame->pts; - av_frame_free(&c->queue[i]); // may be a little heavy for the UI thread? + av_frame_free(&c->queue[i]); // may be a little heavy for the main thread? c->queue.removeAt(i); i--; }