added option to disable multithreading on images

This commit is contained in:
itsmattkc
2019-01-07 11:37:37 +11:00
parent 589c3e9df1
commit fee2c457f2
7 changed files with 230 additions and 201 deletions
+21 -14
View File
@@ -10,6 +10,7 @@
#include <QLabel>
#include <QComboBox>
#include <QGroupBox>
#include <QCheckBox>
#include <QRadioButton>
#include <QDialogButtonBox>
#include <QTreeWidget>
@@ -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;i<key_shortcut_fields.size();i++) {
@@ -177,24 +179,29 @@ void PreferencesDialog::setup_ui() {
tabWidget->addTab(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();
+2
View File
@@ -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<QAction*> key_shortcut_actions;
QVector<QTreeWidgetItem*> key_shortcut_items;
+6 -1
View File
@@ -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
+1
View File
@@ -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);
+186 -179
View File
@@ -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<FootageStream>& stream_list = (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ? footage->audio_tracks : footage->video_tracks;
for (int j=0;j<stream_list.size();j++) {
if (stream_list.at(j).file_index == i) {
stream_list[j] = ms;
append = false;
}
}
if (append) stream_list.append(ms);
}
}
}
footage->length = fmt_ctx->duration;
if (append) {
QVector<FootageStream>& stream_list = (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ? footage->audio_tracks : footage->video_tracks;
for (int j=0;j<stream_list.size();j++) {
if (stream_list.at(j).file_index == i) {
stream_list[j] = ms;
append = false;
}
}
if (append) stream_list.append(ms);
}
}
}
footage->length = 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;i<footage->video_tracks.size();i++) {
FootageStream& ms = footage->video_tracks[i];
for (int i=0;i<footage->video_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;i<footage->audio_tracks.size();i++) {
FootageStream& ms = footage->audio_tracks[i];
for (int i=0;i<footage->audio_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;j<data.size();j++) {
// faster way?
ms.audio_preview[j] = data.at(j);
ms.audio_preview[j] = data.at(j);
}
ms.preview_done = true;
ms.preview_done = true;
f.close();
} else {
found = false;
@@ -165,56 +172,56 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) {
}
}
if (!found) {
for (int i=0;i<footage->video_tracks.size();i++) {
FootageStream& ms = footage->video_tracks[i];
ms.preview_done = false;
for (int i=0;i<footage->video_tracks.size();i++) {
FootageStream& ms = footage->video_tracks[i];
ms.preview_done = false;
}
for (int i=0;i<footage->audio_tracks.size();i++) {
FootageStream& ms = footage->audio_tracks[i];
ms.audio_preview.clear();
ms.preview_done = false;
for (int i=0;i<footage->audio_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;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);
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);
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<AVPixelFormat>(temp_frame->format),
dstW,
dstH,
static_cast<AVPixelFormat>(AV_PIX_FMT_RGBA),
static_cast<AVPixelFormat>(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;i<footage->video_tracks.size();i++) {
if (!footage->video_tracks.at(i).preview_done) {
for (int i=0;i<footage->video_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;i<footage->audio_tracks.size();i++) {
footage->audio_tracks[i].preview_done = true;
}
av_frame_free(&temp_frame);
}
}
for (int i=0;i<footage->audio_tracks.size();i++) {
footage->audio_tracks[i].preview_done = true;
}
av_frame_free(&temp_frame);
av_packet_free(&packet);
for (unsigned int i=0;i<fmt_ctx->nb_streams;i++) {
if (codec_ctx[i] != NULL) {
avcodec_close(codec_ctx[i]);
}
for (unsigned int i=0;i<fmt_ctx->nb_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;i<fmt_ctx->nb_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;i<footage->video_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;i<footage->video_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;i<footage->audio_tracks.size();i++) {
FootageStream& ms = footage->audio_tracks[i];
for (int i=0;i<footage->audio_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() {
+8 -4
View File
@@ -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) {
+6 -3
View File
@@ -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--;
}