Merge pull request #325 from olive-editor/threadedgl2
move renderer to separate thread
This commit is contained in:
+35
-28
@@ -6,9 +6,12 @@
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "ui/renderthread.h"
|
||||
#include "ui/renderfunctions.h"
|
||||
#include "playback/playback.h"
|
||||
#include "playback/audio.h"
|
||||
#include "dialogs/exportdialog.h"
|
||||
#include "mainwindow.h"
|
||||
#include "debug.h"
|
||||
|
||||
extern "C" {
|
||||
@@ -111,7 +114,7 @@ bool ExportThread::setupVideo() {
|
||||
vcodec_ctx->sample_aspect_ratio = {1, 1};
|
||||
vcodec_ctx->pix_fmt = vcodec->pix_fmts[0]; // maybe be breakable code
|
||||
vcodec_ctx->framerate = av_d2q(video_frame_rate, INT_MAX);
|
||||
if (video_compression_type == COMPRESSION_TYPE_CBR) vcodec_ctx->bit_rate = video_bitrate * 1000000;
|
||||
if (video_compression_type == COMPRESSION_TYPE_CBR) vcodec_ctx->bit_rate = qRound(video_bitrate * 1000000);
|
||||
vcodec_ctx->time_base = av_inv_q(vcodec_ctx->framerate);
|
||||
video_stream->time_base = vcodec_ctx->time_base;
|
||||
|
||||
@@ -312,12 +315,7 @@ bool ExportThread::setupContainer() {
|
||||
|
||||
void ExportThread::run() {
|
||||
panel_sequence_viewer->pause();
|
||||
|
||||
if (!panel_sequence_viewer->viewer_widget->context()->makeCurrent(&surface)) {
|
||||
qCritical() << "Make current failed";
|
||||
ed->export_error = tr("could not make OpenGL context current");
|
||||
return;
|
||||
}
|
||||
panel_sequence_viewer->seek(start_frame);
|
||||
|
||||
// copy filename
|
||||
QByteArray ba = filename.toUtf8();
|
||||
@@ -339,28 +337,35 @@ void ExportThread::run() {
|
||||
}
|
||||
}
|
||||
|
||||
panel_sequence_viewer->seek(start_frame);
|
||||
panel_sequence_viewer->reset_all_audio();
|
||||
|
||||
QOpenGLFramebufferObject fbo(sequence->width, sequence->height, QOpenGLFramebufferObject::CombinedDepthStencil, GL_TEXTURE_RECTANGLE);
|
||||
fbo.bind();
|
||||
|
||||
panel_sequence_viewer->viewer_widget->default_fbo = &fbo;
|
||||
|
||||
long file_audio_samples = 0;
|
||||
qint64 start_time, frame_time, avg_time, eta, total_time = 0;
|
||||
long remaining_frames, frame_count = 1;
|
||||
|
||||
RenderThread* renderer = panel_sequence_viewer->viewer_widget->get_renderer();
|
||||
disconnect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget, SLOT(queue_repaint()));
|
||||
connect(renderer, SIGNAL(ready()), this, SLOT(wake()));
|
||||
|
||||
mutex.lock();
|
||||
|
||||
while (sequence->playhead <= end_frame && continueEncode) {
|
||||
start_time = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
panel_sequence_viewer->viewer_widget->paintGL();
|
||||
if (audio_enabled) {
|
||||
compose_audio(nullptr, sequence, true);
|
||||
}
|
||||
if (video_enabled) {
|
||||
do {
|
||||
// TODO optimize by rendering the next frame while encoding the last
|
||||
renderer->start_render(nullptr, sequence, nullptr, video_frame->data[0]);
|
||||
waitCond.wait(&mutex);
|
||||
if (!continueEncode) break;
|
||||
} while (renderer->did_texture_fail());
|
||||
if (!continueEncode) break;
|
||||
}
|
||||
|
||||
// encode last frame while rendering next frame
|
||||
double timecode_secs = (double) (sequence->playhead-start_frame) / sequence->frame_rate;
|
||||
if (video_enabled) {
|
||||
// get image from opengl
|
||||
glReadPixels(0, 0, video_frame->linesize[0]/4, sequence->height, GL_RGBA, GL_UNSIGNED_BYTE, video_frame->data[0]);
|
||||
|
||||
// change pixel format
|
||||
sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize);
|
||||
sws_frame->pts = qRound(timecode_secs/av_q2d(video_stream->time_base));
|
||||
@@ -371,6 +376,7 @@ void ExportThread::run() {
|
||||
if (audio_enabled) {
|
||||
// do we need to encode more audio samples?
|
||||
while (continueEncode && file_audio_samples <= (timecode_secs*audio_sampling_rate)) {
|
||||
// copy samples from audio buffer to AVFrame
|
||||
int adjusted_read = audio_ibuffer_read%audio_ibuffer_size;
|
||||
int copylen = qMin(aframe_bytes, audio_ibuffer_size-adjusted_read);
|
||||
memcpy(audio_frame->data[0], audio_ibuffer+adjusted_read, copylen);
|
||||
@@ -405,20 +411,22 @@ void ExportThread::run() {
|
||||
|
||||
// qInfo() << "Encoded frame" << sequence->playhead << "- took" << frame_time << "ms (avg:" << avg_time << "ms, remaining:" << remaining_frames << ", ETA:" << eta << ")";
|
||||
|
||||
emit progress_changed(qRound(((double) (sequence->playhead-start_frame) / (double) (end_frame-start_frame)) * 100), eta);
|
||||
emit progress_changed(qRound((double(sequence->playhead-start_frame) / double(end_frame-start_frame)) * 100.0), eta);
|
||||
sequence->playhead++;
|
||||
frame_count++;
|
||||
}
|
||||
|
||||
disconnect(renderer, SIGNAL(ready()), this, SLOT(wake()));
|
||||
connect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget, SLOT(queue_repaint()));
|
||||
|
||||
mutex.unlock();
|
||||
|
||||
if (continueEncode) {
|
||||
if (video_enabled) vpkt_alloc = true;
|
||||
if (audio_enabled) apkt_alloc = true;
|
||||
}
|
||||
|
||||
panel_sequence_viewer->viewer_widget->default_fbo = nullptr;
|
||||
rendering = false;
|
||||
|
||||
fbo.release();
|
||||
mainWindow->set_rendering_state(false);
|
||||
|
||||
if (audio_enabled && continueEncode) {
|
||||
// flush swresample
|
||||
@@ -478,9 +486,8 @@ void ExportThread::run() {
|
||||
}
|
||||
|
||||
delete [] c_filename;
|
||||
}
|
||||
|
||||
panel_sequence_viewer->viewer_widget->context()->doneCurrent();
|
||||
panel_sequence_viewer->viewer_widget->context()->moveToThread(qApp->thread());
|
||||
|
||||
rendering = false;
|
||||
void ExportThread::wake() {
|
||||
waitCond.wakeAll();
|
||||
}
|
||||
|
||||
+12
-5
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <QThread>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
|
||||
class ExportDialog;
|
||||
struct AVFormatContext;
|
||||
@@ -52,19 +54,21 @@ public:
|
||||
bool continueEncode;
|
||||
signals:
|
||||
void progress_changed(int value, qint64 remaining_ms);
|
||||
public slots:
|
||||
void wake();
|
||||
private:
|
||||
bool encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream, bool rescale);
|
||||
bool setupVideo();
|
||||
bool setupAudio();
|
||||
bool setupContainer();
|
||||
|
||||
AVFormatContext* fmt_ctx;
|
||||
AVFormatContext* fmt_ctx;
|
||||
AVStream* video_stream;
|
||||
AVCodec* vcodec;
|
||||
AVCodecContext* vcodec_ctx;
|
||||
AVFrame* video_frame;
|
||||
AVFrame* sws_frame;
|
||||
SwsContext* sws_ctx;
|
||||
SwsContext* sws_ctx;
|
||||
AVStream* audio_stream;
|
||||
AVCodec* acodec;
|
||||
AVFrame* audio_frame;
|
||||
@@ -72,14 +76,17 @@ private:
|
||||
AVCodecContext* acodec_ctx;
|
||||
AVPacket video_pkt;
|
||||
AVPacket audio_pkt;
|
||||
SwrContext* swr_ctx;
|
||||
SwrContext* swr_ctx;
|
||||
|
||||
bool vpkt_alloc;
|
||||
bool apkt_alloc;
|
||||
bool vpkt_alloc;
|
||||
bool apkt_alloc;
|
||||
|
||||
int aframe_bytes;
|
||||
int ret;
|
||||
char* c_filename;
|
||||
|
||||
QMutex mutex;
|
||||
QWaitCondition waitCond;
|
||||
};
|
||||
|
||||
#endif // EXPORTTHREAD_H
|
||||
|
||||
+20
-20
@@ -153,12 +153,12 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
if (type == LOAD_TYPE_VERSION) {
|
||||
int proj_version = stream.readElementText().toInt();
|
||||
if (proj_version < MIN_SAVE_VERSION && proj_version > SAVE_VERSION) {
|
||||
if (QMessageBox::warning(
|
||||
mainWindow,
|
||||
tr("Version Mismatch"),
|
||||
tr("This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?"),
|
||||
QMessageBox::Yes,
|
||||
QMessageBox::No) == QMessageBox::No) {
|
||||
if (QMessageBox::warning(
|
||||
mainWindow,
|
||||
tr("Version Mismatch"),
|
||||
tr("This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?"),
|
||||
QMessageBox::Yes,
|
||||
QMessageBox::No) == QMessageBox::No) {
|
||||
show_err = false;
|
||||
return false;
|
||||
}
|
||||
@@ -444,11 +444,11 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
if (!found) {
|
||||
correct_clip->linked.removeAt(j);
|
||||
j--;
|
||||
if (QMessageBox::warning(mainWindow,
|
||||
tr("Invalid Clip Link"),
|
||||
tr("This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?"),
|
||||
QMessageBox::Yes,
|
||||
QMessageBox::No) == QMessageBox::No) {
|
||||
if (QMessageBox::warning(mainWindow,
|
||||
tr("Invalid Clip Link"),
|
||||
tr("This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?"),
|
||||
QMessageBox::Yes,
|
||||
QMessageBox::No) == QMessageBox::No) {
|
||||
delete s;
|
||||
return false;
|
||||
}
|
||||
@@ -601,7 +601,7 @@ void LoadThread::run() {
|
||||
xml_error = false;
|
||||
if (show_err) emit error();
|
||||
} else if (stream.hasError()) {
|
||||
error_str = tr("%1 - Line: %2 Col: %3").arg(stream.errorString(), QString::number(stream.lineNumber()), QString::number(stream.columnNumber()));
|
||||
error_str = tr("%1 - Line: %2 Col: %3").arg(stream.errorString(), QString::number(stream.lineNumber()), QString::number(stream.columnNumber()));
|
||||
xml_error = true;
|
||||
emit error();
|
||||
cont = false;
|
||||
@@ -641,15 +641,15 @@ void LoadThread::cancel() {
|
||||
void LoadThread::error_func() {
|
||||
if (xml_error) {
|
||||
qCritical() << "Error parsing XML." << error_str;
|
||||
QMessageBox::critical(mainWindow,
|
||||
tr("XML Parsing Error"),
|
||||
tr("Couldn't load '%1'. %2").arg(project_url, error_str),
|
||||
QMessageBox::Ok);
|
||||
QMessageBox::critical(mainWindow,
|
||||
tr("XML Parsing Error"),
|
||||
tr("Couldn't load '%1'. %2").arg(project_url, error_str),
|
||||
QMessageBox::Ok);
|
||||
} else {
|
||||
QMessageBox::critical(mainWindow,
|
||||
tr("Project Load Error"),
|
||||
tr("Error loading project: %1").arg(error_str),
|
||||
QMessageBox::Ok);
|
||||
QMessageBox::critical(mainWindow,
|
||||
tr("Project Load Error"),
|
||||
tr("Error loading project: %1").arg(error_str),
|
||||
QMessageBox::Ok);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user