diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 43a3a5d05..044ffd895 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "debug.h" @@ -94,6 +95,10 @@ void PreferencesDialog::save() { config.img_seq_formats = imgSeqFormatEdit->text(); config.fast_seeking = fastSeekButton->isChecked(); config.disable_multithreading_for_images = disable_img_multithread->isChecked(); + config.upcoming_queue_size = upcoming_queue_spinbox->value(); + config.upcoming_queue_type = upcoming_queue_type->currentIndex(); + config.previous_queue_size = previous_queue_spinbox->value(); + config.previous_queue_type = previous_queue_type->currentIndex(); // save keyboard shortcuts for (int i=0;iaddWidget(new QLabel("Image sequence formats:"), 0, 0, 1, 1); + general_layout->addWidget(new QLabel("Image sequence formats:"), 0, 0, 1, 1); - imgSeqFormatEdit = new QLineEdit(tab); + imgSeqFormatEdit = new QLineEdit(general_tab); - gridLayout_2->addWidget(imgSeqFormatEdit, 0, 1, 1, 1); + general_layout->addWidget(imgSeqFormatEdit, 0, 1, 1, 1); - gridLayout_2->addWidget(new QLabel("Audio Recording:"), 1, 0, 1, 1); + general_layout->addWidget(new QLabel("Audio Recording:"), 1, 0, 1, 1); - recordingComboBox = new QComboBox(tab); + recordingComboBox = new QComboBox(general_tab); recordingComboBox->addItem("Mono"); recordingComboBox->addItem("Stereo"); - gridLayout_2->addWidget(recordingComboBox, 1, 1, 1, 1); + general_layout->addWidget(recordingComboBox, 1, 1, 1, 1); - tabWidget->addTab(tab, "General"); - QWidget* tab_2 = new QWidget(); - tabWidget->addTab(tab_2, "Behavior"); + tabWidget->addTab(general_tab, "General"); + QWidget* behavior_tab = new QWidget(); + tabWidget->addTab(behavior_tab, "Behavior"); // Playback QWidget* playback_tab = new QWidget(); QVBoxLayout* playback_tab_layout = new QVBoxLayout(playback_tab); + // 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); + // Playback -> Seeking QGroupBox* seeking_group = new QGroupBox(playback_tab); seeking_group->setTitle("Seeking"); @@ -197,10 +207,29 @@ void PreferencesDialog::setup_ui() { seeking_group_layout->addWidget(fastSeekButton); playback_tab_layout->addWidget(seeking_group); - // 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); + // Playback -> Memory Usage + QGroupBox* memory_usage_group = new QGroupBox(playback_tab); + memory_usage_group->setTitle("Memory Usage"); + QGridLayout* memory_usage_layout = new QGridLayout(memory_usage_group); + memory_usage_layout->addWidget(new QLabel("Upcoming Frame Queue:"), 0, 0); + upcoming_queue_spinbox = new QDoubleSpinBox(); + upcoming_queue_spinbox->setValue(config.upcoming_queue_size); + memory_usage_layout->addWidget(upcoming_queue_spinbox, 0, 1); + upcoming_queue_type = new QComboBox(); + upcoming_queue_type->addItem("frames"); + upcoming_queue_type->addItem("seconds"); + upcoming_queue_type->setCurrentIndex(config.upcoming_queue_type); + memory_usage_layout->addWidget(upcoming_queue_type, 0, 2); + memory_usage_layout->addWidget(new QLabel("Previous Frame Queue:"), 1, 0); + previous_queue_spinbox = new QDoubleSpinBox(); + previous_queue_spinbox->setValue(config.previous_queue_size); + memory_usage_layout->addWidget(previous_queue_spinbox, 1, 1); + previous_queue_type = new QComboBox(); + previous_queue_type->addItem("frames"); + previous_queue_type->addItem("seconds"); + previous_queue_type->setCurrentIndex(config.previous_queue_type); + memory_usage_layout->addWidget(previous_queue_type, 1, 2); + playback_tab_layout->addWidget(memory_usage_group); tabWidget->addTab(playback_tab, "Playback"); diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index 4db3f5037..0e6d575d4 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -11,6 +11,7 @@ class QTreeWidget; class QTreeWidgetItem; class QMenu; class QCheckBox; +class QDoubleSpinBox; class KeySequenceEditor : public QKeySequenceEdit { Q_OBJECT @@ -46,6 +47,10 @@ private: QRadioButton* fastSeekButton; QTreeWidget* keyboard_tree; QCheckBox* disable_img_multithread; + QDoubleSpinBox* upcoming_queue_spinbox; + QComboBox* upcoming_queue_type; + QDoubleSpinBox* previous_queue_spinbox; + QComboBox* previous_queue_type; QVector key_shortcut_actions; QVector key_shortcut_items; diff --git a/io/config.cpp b/io/config.cpp index 00e902719..1b243a0ca 100644 --- a/io/config.cpp +++ b/io/config.cpp @@ -39,7 +39,11 @@ Config::Config() project_view_type(PROJECT_VIEW_TREE), set_name_with_marker(true), show_project_toolbar(false), - disable_multithreading_for_images(false) + disable_multithreading_for_images(false), + previous_queue_size(3), + previous_queue_type(FRAME_QUEUE_TYPE_FRAMES), + upcoming_queue_size(0.5), + upcoming_queue_type(FRAME_QUEUE_TYPE_SECONDS) {} void Config::load(QString path) { @@ -134,6 +138,18 @@ void Config::load(QString path) { } else if (stream.name() == "DisableMultithreadedImages") { stream.readNext(); disable_multithreading_for_images = (stream.text() == "1"); + } else if (stream.name() == "PreviousFrameQueueSize") { + stream.readNext(); + previous_queue_size = stream.text().toDouble(); + } else if (stream.name() == "PreviousFrameQueueType") { + stream.readNext(); + previous_queue_type = stream.text().toInt(); + } else if (stream.name() == "UpcomingFrameQueueSize") { + stream.readNext(); + upcoming_queue_size = stream.text().toDouble(); + } else if (stream.name() == "UpcomingFrameQueueType") { + stream.readNext(); + upcoming_queue_type = stream.text().toInt(); } } } @@ -186,6 +202,10 @@ void Config::save(QString path) { 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.writeTextElement("PreviousFrameQueueSize", QString::number(previous_queue_size)); + stream.writeTextElement("PreviousFrameQueueType", QString::number(previous_queue_type)); + stream.writeTextElement("UpcomingFrameQueueSize", QString::number(upcoming_queue_size)); + stream.writeTextElement("UpcomingFrameQueueType", QString::number(upcoming_queue_type)); stream.writeEndElement(); // configuration stream.writeEndDocument(); // doc diff --git a/io/config.h b/io/config.h index 25245e89f..f1a6e1d48 100644 --- a/io/config.h +++ b/io/config.h @@ -21,6 +21,9 @@ #define PROJECT_VIEW_TREE 0 #define PROJECT_VIEW_ICON 1 +#define FRAME_QUEUE_TYPE_FRAMES 0 +#define FRAME_QUEUE_TYPE_SECONDS 1 + struct Config { Config(); bool saved_layout; @@ -51,6 +54,10 @@ struct Config { bool set_name_with_marker; bool show_project_toolbar; bool disable_multithreading_for_images; + double previous_queue_size; + int previous_queue_type; + double upcoming_queue_size; + int upcoming_queue_type; void load(QString path); void save(QString path); diff --git a/playback/cacher.cpp b/playback/cacher.cpp index 2450b6943..e172d534f 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -674,7 +674,22 @@ void open_clip_worker(Clip* clip) { 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 * m->speed * 0.5); + if (ms->infinite_length) { + clip->max_queue_size = 1; + } else { + clip->max_queue_size = 0; + if (config.upcoming_queue_type == FRAME_QUEUE_TYPE_FRAMES) { + clip->max_queue_size += qCeil(config.upcoming_queue_size); + } else { + clip->max_queue_size += qCeil(ms->video_frame_rate * m->speed * config.upcoming_queue_size); + } + if (config.previous_queue_type == FRAME_QUEUE_TYPE_FRAMES) { + clip->max_queue_size += qCeil(config.previous_queue_size); + } else { + clip->max_queue_size += qCeil(ms->video_frame_rate * m->speed * config.previous_queue_size); + } + } + if (ms->video_interlacing != VIDEO_PROGRESSIVE) clip->max_queue_size *= 2; clip->opts = NULL; diff --git a/playback/playback.cpp b/playback/playback.cpp index 9cc1daf7b..e8a64a0c7 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -163,23 +163,44 @@ void get_clip_frame(Clip* c, long playhead) { target_frame = c->queue.at(closest_frame); int64_t next_pts = INT64_MAX; int64_t minimum_ts = target_frame->pts; - /*if (c->reverse) { - minimum_ts += quarter_pts; - } else { - minimum_ts -= quarter_pts; - }*/ + + int previous_frame_count = 0; + if (config.previous_queue_type == FRAME_QUEUE_TYPE_SECONDS) { + minimum_ts -= (second_pts*config.previous_queue_size); + } + //dout << "closest frame was" << closest_frame << "with" << target_frame->pts << "/" << target_pts; for (int i=0;iqueue.size();i++) { if (c->queue.at(i)->pts > target_frame->pts && c->queue.at(i)->pts < next_pts) { next_pts = c->queue.at(i)->pts; } 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 main thread? - c->queue.removeAt(i); - i--; + if (config.previous_queue_type == FRAME_QUEUE_TYPE_SECONDS) { + //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 main thread? + c->queue.removeAt(i); + i--; + } else { + // TODO sort from largest to smallest + previous_frame_count++; + } } } + + if (config.previous_queue_type == FRAME_QUEUE_TYPE_FRAMES) { + while (previous_frame_count > qCeil(config.previous_queue_size)) { + int smallest = 0; + for (int i=1;iqueue.size();i++) { + if (c->queue.at(i)->pts < c->queue.at(smallest)->pts) { + smallest = i; + } + } + av_frame_free(&c->queue[smallest]); + c->queue.removeAt(smallest); + previous_frame_count--; + } + } + if (next_pts == INT64_MAX) next_pts = target_frame->pts + target_frame->pkt_duration; // we didn't get the exact timestamp