most proxy behavior is complete

This commit is contained in:
itsmattkc
2019-02-01 01:17:22 +11:00
parent c779ec43e7
commit 7a22065162
13 changed files with 245 additions and 23 deletions
+3
View File
@@ -108,6 +108,9 @@ void ProxyDialog::accept() {
// all proxy info checks out, queue it with the proxy generator
for (int i=0;i<info_list.size();i++) {
info_list.at(i).footage->proxy = true;
info_list.at(i).footage->proxy_path.clear();
proxy_generator.queue(info_list.at(i));
}
+1 -1
View File
@@ -3,7 +3,7 @@
#include <QString>
#define SAVE_VERSION 190120 // YYMMDD
#define SAVE_VERSION 190201 // YYMMDD
#define MIN_SAVE_VERSION 190104 // lowest compatible project version
#define TIMECODE_DROP 0
+4
View File
@@ -250,6 +250,10 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
m->speed = attr.value().toDouble();
} else if (attr.name() == "alphapremul") {
m->alpha_is_premultiplied = (attr.value() == "1");
} else if (attr.name() == "proxy") {
m->proxy = (attr.value() == "1");
} else if (attr.name() == "proxypath") {
m->proxy_path = attr.value().toString();
}
}
+56 -9
View File
@@ -2,10 +2,12 @@
#include "project/footage.h"
#include "io/path.h"
#include "mainwindow.h"
#include <QDir>
#include <QFileInfo>
#include <QtMath>
#include <QStatusBar>
#include <QDebug>
@@ -19,7 +21,10 @@ enum AVCodecID temp_enc_codec = AV_CODEC_ID_PRORES;
ProxyGenerator::ProxyGenerator() : cancelled(false) {}
void transcode(const ProxyInfo& info) {
void ProxyGenerator::transcode(const ProxyInfo& info) {
// set progress to 0
current_progress = 0.0;
// open input file
AVFormatContext* input_fmt_ctx = nullptr;
avformat_open_input(&input_fmt_ctx, info.footage->url.toUtf8(), nullptr, nullptr);
@@ -145,7 +150,7 @@ void transcode(const ProxyInfo& info) {
AVFrame* dec_frame = av_frame_alloc();
// main transcoding loop
while (true) {
while (!skip) {
// cache stream index
int stream_index = packet.stream_index;
@@ -186,28 +191,30 @@ void transcode(const ProxyInfo& info) {
// send packet to decoder
avcodec_send_packet(input_streams.at(stream_index), &packet);
// use timestamp and stream duration to create a rough estimation of the progress through this file
current_progress = qCeil((double(packet.pts)/double(input_fmt_ctx->streams[packet.stream_index]->duration))*100);
}
// free packet allocated by av_read_frame
av_packet_unref(&packet);
} while ((recfr_ret = avcodec_receive_frame(input_streams.at(packet.stream_index), dec_frame)) == AVERROR(EAGAIN));
} while ((recfr_ret = avcodec_receive_frame(input_streams.at(packet.stream_index), dec_frame)) == AVERROR(EAGAIN) && !skip);
// error/eof handling - cancel while loop
if (read_ret < 0) {
if (read_ret < 0 || skip) {
break;
}
//
// SWSCALE IF NECESSARY
//
// free packet as we're about to use it for encoding
av_packet_unref(&packet);
// rescale input frame timestamp to output timestamp
av_rescale_q(dec_frame->pts, input_fmt_ctx->streams[stream_index]->time_base, output_fmt_ctx->streams[stream_index]->time_base);
// determine if the pix_fmt is different, so if we need to convert
bool convert_pix_fmt = (output_streams.at(stream_index)->pix_fmt != input_streams.at(stream_index)->pix_fmt);
// create reference to the frame to be sent to the encoder
AVFrame* enc_frame = dec_frame;
if (convert_pix_fmt) {
@@ -233,14 +240,21 @@ void transcode(const ProxyInfo& info) {
av_frame_free(&enc_frame);
}
// return value for packet receiving
int recret;
while ((recret = avcodec_receive_packet(output_streams.at(stream_index), &packet)) >= 0) {
// loop through receiving packets
while ((recret = avcodec_receive_packet(output_streams.at(stream_index), &packet)) >= 0 && !skip) {
// set packet stream index to current stream index
packet.stream_index = stream_index;
// write frame to file
av_interleaved_write_frame(output_fmt_ctx, &packet);
// unref old packet
av_packet_unref(&packet);
}
}
@@ -276,7 +290,13 @@ void transcode(const ProxyInfo& info) {
// close input file
avformat_close_input(&input_fmt_ctx);
// set footage to use newly generated proxy
info.footage->proxy = true;
info.footage->proxy_path = info.path;
qInfo() << "Finished creating proxy for" << info.footage->url;
mainWindow->statusBar()->showMessage(tr("Finished generating proxy for \"%1\"").arg(info.footage->url));
}
// main proxy generating loop
@@ -300,6 +320,9 @@ void ProxyGenerator::run() {
// create directory for info
QFileInfo(info.path).dir().mkpath(".");
// set skip to false
skip = false;
// transcode proxy
transcode(info);
@@ -317,6 +340,22 @@ void ProxyGenerator::run() {
// called to add footage to generate proxies for
void ProxyGenerator::queue(const ProxyInfo &info) {
// remove any queued proxies with the same footage
if (!proxy_queue.isEmpty()
&& proxy_queue.first().footage == info.footage) {
// if the thread is currently processing a proxy with the same footage, abort it
skip = true;
}
// scan through the rest of the queue for another proxy with the same footage (start with 1 since we already processed first())
for (int i=1;i<proxy_queue.size();i++) {
if (proxy_queue.at(i).footage == info.footage) {
// found a duplicate, assume the one we're queuing now overrides and delete it
proxy_queue.removeAt(i);
i--;
}
}
// add proxy info to queue
proxy_queue.append(info);
@@ -328,6 +367,7 @@ void ProxyGenerator::queue(const ProxyInfo &info) {
void ProxyGenerator::cancel() {
// signal to thread to cancel
cancelled = true;
skip = true;
// if signal is sleeping, wake it to cancel correctly
waitCond.wakeAll();
@@ -336,5 +376,12 @@ void ProxyGenerator::cancel() {
wait();
}
double ProxyGenerator::get_proxy_progress(Footage *f) {
if (proxy_queue.first().footage == f) {
return current_progress;
}
return 0.0;
}
// proxy generator is a global omnipotent entity
ProxyGenerator proxy_generator;
+15
View File
@@ -22,11 +22,26 @@ public:
void run();
void queue(const ProxyInfo& info);
void cancel();
double get_proxy_progress(Footage* f);
private:
// queue of footage to process proxies for
QVector<ProxyInfo> proxy_queue;
// threading objects
QWaitCondition waitCond;
QMutex mutex;
// set to true if you want to permanently close ProxyGenerator
bool cancelled;
// set to true if you want to abort the footage currently being processed
bool skip;
// stores progress in percent of proxy currently being processed
double current_progress;
// function that performs the actual transcode
void transcode(const ProxyInfo& info);
};
// proxy generator is a global omnipotent entity
+4
View File
@@ -954,6 +954,10 @@ void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only,
stream.writeAttribute("out", QString::number(f->out));
stream.writeAttribute("speed", QString::number(f->speed));
stream.writeAttribute("alphapremul", QString::number(f->alpha_is_premultiplied));
stream.writeAttribute("proxy", QString::number(f->proxy));
stream.writeAttribute("proxypath", f->proxy_path);
for (int j=0;j<f->video_tracks.size();j++) {
const FootageStream& ms = f->video_tracks.at(j);
stream.writeStartElement("video");
+13 -1
View File
@@ -673,7 +673,19 @@ void open_clip_worker(Clip* clip) {
} else if (clip->media->get_type() == MEDIA_TYPE_FOOTAGE) {
// opens file resource for FFmpeg and prepares Clip struct for playback
Footage* m = clip->media->to_footage();
QByteArray ba = m->url.toUtf8();
// byte array for retriving raw bytes from QString URL
QByteArray ba;
// do we have a proxy?
if (m->proxy
&& !m->proxy_path.isEmpty()
&& QFileInfo::exists(m->proxy_path)) {
ba = m->proxy_path.toUtf8();
} else {
ba = m->url.toUtf8();
}
const char* filename = ba.constData();
const FootageStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream);
+47 -3
View File
@@ -13,6 +13,7 @@
#include "project/media.h"
#include "io/config.h"
#include "io/avtogl.h"
#include "io/proxygenerator.h"
#include "debug.h"
extern "C" {
@@ -28,6 +29,8 @@ extern "C" {
#include <QOpenGLTexture>
#include <QOpenGLPixelTransferOptions>
#include <QOpenGLFramebufferObject>
#include <QPainter>
#include <QCoreApplication>
#ifdef QT_DEBUG
//#define GCF_DEBUG
@@ -272,14 +275,55 @@ void get_clip_frame(Clip* c, long playhead, bool& texture_failed) {
uint8_t* data_buffer_1 = target_frame->data[0];
uint8_t* data_buffer_2 = nullptr;
size_t frame_size;
size_t frame_size = size_t(target_frame->linesize[0])*size_t(target_frame->height);
// if proxy is currently being generated, show an on-screen message of its progress
if (c->media->to_footage()->proxy
&& c->media->to_footage()->proxy_path.isEmpty()) {
// create buffers to draw on
data_buffer_1 = new uint8_t[frame_size];
data_buffer_2 = new uint8_t[frame_size];
memcpy(data_buffer_1, target_frame->data[0], frame_size);
// wrap data in a QImage for painting
QImage img(data_buffer_1, target_frame->width, target_frame->height, QImage::Format_RGBA8888);
// create QPainter process
QPainter p(&img);
// set font color to white
p.setPen(Qt::white);
// set font size relative to frame size (divided by 12)
QFont overlay_font = p.font();
overlay_font.setPixelSize(target_frame->height/12);
p.setFont(overlay_font);
// generate overlay text
QString proxy_overlay_text = QCoreApplication::translate("Playback", "Generating Proxy: %1%").arg(proxy_generator.get_proxy_progress(c->media->to_footage()));
int text_height = p.fontMetrics().descent() + p.fontMetrics().height();
// draw semi-transparent black background
p.fillRect(QRect(0,
target_frame->height-text_height,
p.fontMetrics().width(proxy_overlay_text),
text_height),
QColor(0, 0, 0, 128)
);
// draw text
p.drawText(0,
target_frame->height-p.fontMetrics().descent(),
proxy_overlay_text);
}
for (int i=0;i<c->effects.size();i++) {
Effect* e = c->effects.at(i);
if (e->enable_image && e->is_enabled()) {
if (data_buffer_1 == target_frame->data[0]) {
frame_size = size_t(target_frame->linesize[0])*size_t(target_frame->height);
data_buffer_1 = new uint8_t[frame_size];
data_buffer_2 = new uint8_t[frame_size];
+2 -1
View File
@@ -18,7 +18,8 @@ Footage::Footage() :
in(0),
out(0),
speed(1.0),
alpha_is_premultiplied(false)
alpha_is_premultiplied(false),
proxy(false)
{
ready_lock.lock();
}
+4
View File
@@ -56,6 +56,10 @@ struct Footage {
double speed;
bool alpha_is_premultiplied;
// proxy config
bool proxy;
QString proxy_path;
PreviewGenerator* preview_gen;
QMutex ready_lock;
+93 -7
View File
@@ -3,13 +3,17 @@
#include "panels/panels.h"
#include "project/media.h"
#include "project/undo.h"
#include "playback/playback.h"
#include "panels/timeline.h"
#include "panels/project.h"
#include "project/footage.h"
#include "panels/viewer.h"
#include "project/projectfilter.h"
#include "project/sequence.h"
#include "io/config.h"
#include "dialogs/proxydialog.h"
#include "ui/viewerwidget.h"
#include "io/proxygenerator.h"
#include "mainwindow.h"
#include <QProcess>
@@ -19,6 +23,8 @@
#include <QMessageBox>
#include <QDesktopServices>
#include <QDebug>
SourcesCommon::SourcesCommon(Project* parent) :
editing_item(nullptr),
project_parent(parent)
@@ -76,11 +82,11 @@ void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& it
connect(show_sequences, SIGNAL(triggered(bool)), panel_project->sorter, SLOT(set_show_sequences(bool)));
if (items.size() > 0) {
Media* m = project_parent->item_to_media(items.at(0));
if (items.size() == 1) {
Media* first_media = project_parent->item_to_media(items.at(0));
// replace footage
int type = m->get_type();
int type = first_media->get_type();
if (type == MEDIA_TYPE_FOOTAGE) {
QAction* replace_action = menu.addAction(tr("Replace/Relink Media"));
QObject::connect(replace_action, SIGNAL(triggered(bool)), project_parent, SLOT(replace_selected_file()));
@@ -100,11 +106,13 @@ void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& it
}
}
// duplicate item
// analyze selected footage types
bool all_sequences = true;
bool all_footage = true;
cached_selected_footage.clear();
for (int i=0;i<items.size();i++) {
Media* m = project_parent->item_to_media(items.at(i));
if (m->get_type() != MEDIA_TYPE_SEQUENCE) {
all_sequences = false;
}
@@ -132,9 +140,52 @@ void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& it
QObject::connect(delete_footage_from_sequences, SIGNAL(triggered(bool)), project_parent, SLOT(delete_clips_using_selected_media()));
QMenu* proxies = menu.addMenu(tr("Proxy"));
proxies->addAction(tr("Create Proxy"), this, SLOT(open_create_proxy_dialog()));
// proxies->addAction(tr("Modify Proxy"));
// proxies->addAction(tr("Restore Original"));
// special case if one footage item is selected and its proxy is currently being generated
if (cached_selected_footage.size() == 1
&& cached_selected_footage.at(0)->proxy
&& cached_selected_footage.at(0)->proxy_path.isEmpty()) {
QAction* action = proxies->addAction(tr("Generating proxy: %1% complete").arg(proxy_generator.get_proxy_progress(cached_selected_footage.at(0))));
action->setEnabled(false);
} else {
// determine whether any selected footage has or doesn't have proxies
bool footage_without_proxies_exists = false;
bool footage_with_proxies_exists = false;
for (int i=0;i<cached_selected_footage.size();i++) {
if (cached_selected_footage.at(i)->proxy) {
footage_with_proxies_exists = true;
} else {
footage_without_proxies_exists = true;
}
}
// if footage was selected WITHOUT proxies
if (footage_without_proxies_exists) {
QString create_proxy_text;
if (footage_with_proxies_exists) {
// some of the footage already has proxies, so we use a different string
create_proxy_text = tr("Create/Modify Proxy");
} else {
// none of the footage has proxies
create_proxy_text = tr("Create Proxy");
}
proxies->addAction(create_proxy_text, this, SLOT(open_create_proxy_dialog()));
}
// if footage was selected WITH proxies
if (footage_with_proxies_exists) {
if (!footage_without_proxies_exists) {
// if all the footage has proxies, we didn't make a "Create/Modify" above, so we create one here (but only "modify")
proxies->addAction(tr("Modify Proxy"), this, SLOT(open_create_proxy_dialog()));
}
proxies->addAction(tr("Restore Original"), this, SLOT(clear_proxies_from_selected()));
}
}
}
// delete media
@@ -308,3 +359,38 @@ void SourcesCommon::open_create_proxy_dialog() {
ProxyDialog pd(mainWindow, cached_selected_footage);
pd.exec();
}
void SourcesCommon::clear_proxies_from_selected() {
QList<QString> delete_list;
for (int i=0;i<cached_selected_footage.size();i++) {
Footage* f = cached_selected_footage.at(i);
if (f->proxy && !f->proxy_path.isEmpty()) {
if (QFileInfo::exists(f->proxy_path)) {
if (QMessageBox::question(mainWindow,
tr("Delete proxy"),
tr("Would you like to delete the proxy file \"%1\" as well?").arg(f->proxy_path),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
delete_list.append(f->proxy_path);
}
}
}
f->proxy = false;
f->proxy_path.clear();
}
if (sequence != nullptr) {
// close all clips so we can delete any proxies requested to be deleted
closeActiveClips(sequence);
// delete proxies requested to be deleted
for (int i=0;i<delete_list.size();i++) {
QFile::remove(delete_list.at(i));
}
// update viewer (will re-open active clips with original media)
panel_sequence_viewer->viewer_widget->frame_update();
}
}
+3
View File
@@ -30,7 +30,10 @@ private slots:
void reveal_in_browser();
void rename_interval();
void item_renamed(Media *item);
// proxy functions
void open_create_proxy_dialog();
void clear_proxies_from_selected();
private:
Media* editing_item;
QModelIndex editing_index;
-1
View File
@@ -58,7 +58,6 @@ void RenderThread::run() {
ctx->functions()->glGenFramebuffers(1, &back_buffer_2);
}
// gen texture
if (front_texture == 0 || tex_width != seq->width || tex_height != seq->height) {
// cache texture size