fixed #99
This commit is contained in:
@@ -309,6 +309,8 @@ void ExportDialog::on_pushButton_2_clicked()
|
||||
|
||||
void ExportDialog::render_thread_finished() {
|
||||
prep_ui_for_render(false);
|
||||
panel_viewer->viewer_widget->makeCurrent();
|
||||
panel_viewer->viewer_widget->initializeGL();
|
||||
}
|
||||
|
||||
void ExportDialog::prep_ui_for_render(bool rendering) {
|
||||
@@ -422,6 +424,7 @@ void ExportDialog::on_pushButton_clicked()
|
||||
connect(et, SIGNAL(finished()), this, SLOT(render_thread_finished()));
|
||||
connect(et, SIGNAL(progress_changed(int)), this, SLOT(update_progress_bar(int)));
|
||||
|
||||
panel_viewer->viewer_widget->multithreaded = false;
|
||||
panel_viewer->viewer_widget->enable_paint = false;
|
||||
panel_viewer->viewer_widget->context()->doneCurrent();
|
||||
panel_viewer->viewer_widget->context()->moveToThread(et);
|
||||
@@ -451,7 +454,6 @@ void ExportDialog::update_progress_bar(int value) {
|
||||
ui->progressBar->setValue(value);
|
||||
}
|
||||
|
||||
void ExportDialog::on_renderCancel_clicked()
|
||||
{
|
||||
void ExportDialog::on_renderCancel_clicked() {
|
||||
et->fail = true;
|
||||
}
|
||||
|
||||
+30
-33
@@ -26,7 +26,7 @@ extern "C" {
|
||||
|
||||
bool encode(AVFormatContext* fmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream) {
|
||||
int ret = avcodec_send_frame(codec_ctx, frame);
|
||||
if (ret < 0) {
|
||||
if (ret < 0/* && ret != AVERROR(EAGAIN) && frame != NULL*/) {
|
||||
qDebug() << "[ERROR] Failed to send frame to encoder." << ret;
|
||||
return false;
|
||||
} else {
|
||||
@@ -47,19 +47,17 @@ bool encode(AVFormatContext* fmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame,
|
||||
}
|
||||
|
||||
void ExportThread::run() {
|
||||
av_log_set_level(AV_LOG_DEBUG);
|
||||
panel_timeline->pause();
|
||||
// av_log_set_level(AV_LOG_DEBUG);
|
||||
|
||||
// TODO make customizable
|
||||
long start = 0;
|
||||
long end = sequence->getEndFrame();
|
||||
|
||||
if (panel_viewer->viewer_widget->context()->makeCurrent(&surface)) {
|
||||
qDebug() << "make current succeeded";
|
||||
} else {
|
||||
qDebug() << "make current failed";
|
||||
if (!panel_viewer->viewer_widget->context()->makeCurrent(&surface)) {
|
||||
qDebug() << "[ERROR] Make current failed";
|
||||
return;
|
||||
}
|
||||
panel_viewer->viewer_widget->multithreaded = false;
|
||||
panel_viewer->viewer_widget->initializeOpenGLFunctions();
|
||||
|
||||
AVFormatContext* fmt_ctx = NULL;
|
||||
QByteArray ba = filename.toLatin1();
|
||||
@@ -76,8 +74,7 @@ void ExportThread::run() {
|
||||
AVCodecContext* vcodec_ctx;
|
||||
AVFrame* video_frame;
|
||||
AVFrame* sws_frame;
|
||||
SwsContext* sws_ctx = NULL;
|
||||
uint8_t* flip_buffer;
|
||||
SwsContext* sws_ctx = NULL;
|
||||
AVStream* audio_stream;
|
||||
AVCodec* acodec;
|
||||
AVFrame* audio_frame;
|
||||
@@ -148,7 +145,7 @@ void ExportThread::run() {
|
||||
sws_ctx = sws_getContext(
|
||||
video_width,
|
||||
video_height,
|
||||
AV_PIX_FMT_RGBA,
|
||||
AV_PIX_FMT_RGBA,
|
||||
video_width,
|
||||
video_height,
|
||||
AV_PIX_FMT_YUV420P,
|
||||
@@ -163,7 +160,6 @@ void ExportThread::run() {
|
||||
sws_frame->width = video_frame->width;
|
||||
sws_frame->height = video_frame->height;
|
||||
av_frame_get_buffer(sws_frame, 0);
|
||||
flip_buffer = new uint8_t[video_frame->width * video_frame->height * 4];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,27 +270,21 @@ void ExportThread::run() {
|
||||
QPainter painter(&fbo_dev);
|
||||
painter.beginNativePainting();
|
||||
|
||||
panel_viewer->viewer_widget->initializeGL();
|
||||
panel_viewer->viewer_widget->flip = true;
|
||||
|
||||
long file_audio_samples = 0;
|
||||
|
||||
while (panel_timeline->playhead < end && !fail) {
|
||||
panel_viewer->viewer_widget->update();
|
||||
panel_viewer->viewer_widget->paintGL();
|
||||
|
||||
double timecode_secs = (double) panel_timeline->playhead / sequence->frame_rate;
|
||||
if (video_enabled) {
|
||||
// get image from opengl
|
||||
glReadPixels(0, 0, video_width, video_height, GL_RGBA, GL_UNSIGNED_BYTE, flip_buffer);
|
||||
|
||||
// flip image vertically because opengl sucks ass
|
||||
int linesize = video_width*4;
|
||||
for (int i=video_height-1;i>=0;i--) {
|
||||
int src_index = i*linesize;
|
||||
int dst_index = (video_height-i)*linesize;
|
||||
memcpy(video_frame->data[0]+dst_index, flip_buffer+src_index, linesize);
|
||||
}
|
||||
// get image from opengl
|
||||
glReadPixels(0, 0, video_width, video_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);
|
||||
// qDebug() << video_frame->linesize[0] << sws_frame->linesize[0];
|
||||
sws_frame->pts = round(timecode_secs/av_q2d(video_stream->time_base));
|
||||
|
||||
// send to encoder
|
||||
@@ -331,7 +321,8 @@ void ExportThread::run() {
|
||||
panel_timeline->playhead++;
|
||||
}
|
||||
|
||||
delete [] flip_buffer;
|
||||
panel_viewer->viewer_widget->flip = false;
|
||||
|
||||
painter.endNativePainting();
|
||||
fbo.release();
|
||||
|
||||
@@ -365,14 +356,20 @@ void ExportThread::run() {
|
||||
}
|
||||
}
|
||||
|
||||
avcodec_close(vcodec_ctx);
|
||||
avcodec_close(acodec_ctx);
|
||||
av_packet_unref(&video_pkt);
|
||||
av_packet_unref(&audio_pkt);
|
||||
av_frame_free(&video_frame);
|
||||
av_frame_free(&audio_frame);
|
||||
avcodec_free_context(&vcodec_ctx);
|
||||
avcodec_free_context(&acodec_ctx);
|
||||
if (video_enabled) {
|
||||
avcodec_close(vcodec_ctx);
|
||||
av_packet_unref(&video_pkt);
|
||||
av_frame_free(&video_frame);
|
||||
avcodec_free_context(&vcodec_ctx);
|
||||
}
|
||||
|
||||
if (audio_enabled) {
|
||||
avcodec_close(acodec_ctx);
|
||||
av_packet_unref(&audio_pkt);
|
||||
av_frame_free(&audio_frame);
|
||||
avcodec_free_context(&acodec_ctx);
|
||||
}
|
||||
|
||||
avio_closep(&fmt_ctx->pb);
|
||||
avformat_free_context(fmt_ctx);
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 4.6.2, 2018-07-03T20:47:05. -->
|
||||
<!-- Written by QtCreator 4.6.2, 2018-07-04T12:50:34. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
+2
-2
@@ -301,6 +301,8 @@ void open_clip_worker(Clip* clip) {
|
||||
|
||||
clip->frame = av_frame_alloc();
|
||||
|
||||
clip->finished_opening = true;
|
||||
|
||||
qDebug() << "[INFO] Clip opened on track" << clip->track;
|
||||
}
|
||||
|
||||
@@ -360,8 +362,6 @@ void Cacher::run() {
|
||||
|
||||
open_clip_worker(clip);
|
||||
|
||||
clip->finished_opening = true;
|
||||
|
||||
while (caching) {
|
||||
clip->can_cache.wait(&clip->lock);
|
||||
if (!caching) {
|
||||
|
||||
+16
-10
@@ -25,17 +25,23 @@ extern "C" {
|
||||
bool texture_failed = false;
|
||||
|
||||
void open_clip(Clip* clip, bool multithreaded) {
|
||||
clip->multithreaded = multithreaded;
|
||||
if (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()));
|
||||
if (multithreaded) {
|
||||
if (clip->open_lock.tryLock()) {
|
||||
clip->multithreaded = true;
|
||||
|
||||
clip->cacher->start(QThread::LowPriority);
|
||||
} else {
|
||||
open_clip_worker(clip);
|
||||
clip->lock.unlock();
|
||||
}
|
||||
// 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(QThread::LowPriority);
|
||||
}
|
||||
} else {
|
||||
clip->multithreaded = false;
|
||||
clip->finished_opening = false;
|
||||
clip->open = true;
|
||||
|
||||
open_clip_worker(clip);
|
||||
}
|
||||
}
|
||||
|
||||
void close_clip(Clip* clip) {
|
||||
|
||||
+125
-117
@@ -22,6 +22,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) : QOpenGLWidget(parent)
|
||||
multithreaded = true;
|
||||
enable_paint = true;
|
||||
force_audio = false;
|
||||
flip = false;
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setDepthBufferSize(24);
|
||||
@@ -56,141 +57,148 @@ void ViewerWidget::paintEvent(QPaintEvent *e) {
|
||||
|
||||
void ViewerWidget::paintGL()
|
||||
{
|
||||
if (multithreaded) retry_timer.stop();
|
||||
bool loop = true;
|
||||
while (loop) {
|
||||
loop = false;
|
||||
texture_failed = false;
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
if (multithreaded) retry_timer.stop();
|
||||
|
||||
current_clips.clear();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
Clip* c = sequence->get_clip(i);
|
||||
current_clips.clear();
|
||||
|
||||
// if clip starts within one second and/or hasn't finished yet
|
||||
if (c != NULL) {
|
||||
if (is_clip_active(c, panel_timeline->playhead)) {
|
||||
// if thread is already working, we don't want to touch this,
|
||||
// but we also don't want to hang the UI thread
|
||||
if (!c->open) {
|
||||
if (c->open_lock.tryLock()) {
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
Clip* c = sequence->get_clip(i);
|
||||
|
||||
// if clip starts within one second and/or hasn't finished yet
|
||||
if (c != NULL) {
|
||||
if (is_clip_active(c, panel_timeline->playhead)) {
|
||||
// if thread is already working, we don't want to touch this,
|
||||
// but we also don't want to hang the UI thread
|
||||
if (!c->open) {
|
||||
open_clip(c, multithreaded);
|
||||
}
|
||||
}
|
||||
|
||||
bool added = false;
|
||||
for (int j=0;j<current_clips.size();j++) {
|
||||
if (current_clips.at(j)->track < c->track) {
|
||||
current_clips.insert(j, c);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
current_clips.append(c);
|
||||
}
|
||||
} else if (c->open) {
|
||||
close_clip(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
texture_failed = false;
|
||||
|
||||
bool render_audio = (panel_timeline->playing || force_audio);
|
||||
|
||||
for (int i=0;i<current_clips.size();i++) {
|
||||
Clip* c = current_clips.at(i);
|
||||
|
||||
if (!c->finished_opening) {
|
||||
qDebug() << "[WARNING] Tried to display clip" << i << "but it's closed";
|
||||
texture_failed = true;
|
||||
} else if (is_clip_active(c, panel_timeline->playhead)) {
|
||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
// start preparing cache
|
||||
get_clip_frame(c, panel_timeline->playhead);
|
||||
|
||||
if (c->texture == NULL) {
|
||||
qDebug() << "[WARNING] Texture hasn't been created yet";
|
||||
texture_failed = true;
|
||||
} else if (panel_timeline->playhead >= c->timeline_in) {
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
glLoadIdentity();
|
||||
int half_width = c->sequence->width/2;
|
||||
int half_height = c->sequence->height/2;
|
||||
glOrtho(-half_width, half_width, half_height,- half_height, -1, 1);
|
||||
int anchor_x = c->media_stream->video_width/2;
|
||||
int anchor_y = c->media_stream->video_height/2;
|
||||
|
||||
// perform all transform effects
|
||||
for (int j=0;j<c->effects.size();j++) {
|
||||
c->effects.at(j)->process_gl(&anchor_x, &anchor_y);
|
||||
}
|
||||
|
||||
if (c->opening_transition != NULL) {
|
||||
int transition_progress = panel_timeline->playhead-c->timeline_in;
|
||||
if (transition_progress < c->opening_transition->length) {
|
||||
c->opening_transition->process_transition((float)transition_progress/(float)c->opening_transition->length);
|
||||
bool added = false;
|
||||
for (int j=0;j<current_clips.size();j++) {
|
||||
if (current_clips.at(j)->track < c->track) {
|
||||
current_clips.insert(j, c);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c->closing_transition != NULL) {
|
||||
int transition_progress = c->closing_transition->length-(panel_timeline->playhead-c->timeline_in-c->getLength()+c->closing_transition->length);
|
||||
if (transition_progress < c->closing_transition->length) {
|
||||
c->closing_transition->process_transition((float)transition_progress/(float)c->closing_transition->length);
|
||||
}
|
||||
if (!added) {
|
||||
current_clips.append(c);
|
||||
}
|
||||
|
||||
int anchor_right = c->media_stream->video_width - anchor_x;
|
||||
int anchor_bottom = c->media_stream->video_height - anchor_y;
|
||||
|
||||
c->texture->bind();
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0.0, 0.0);
|
||||
glVertex2f(-anchor_x, -anchor_y);
|
||||
glTexCoord2f(1.0, 0.0);
|
||||
glVertex2f(anchor_right, -anchor_y);
|
||||
glTexCoord2f(1.0, 1.0);
|
||||
glVertex2f(anchor_right, anchor_bottom);
|
||||
glTexCoord2f(0.0, 1.0);
|
||||
glVertex2f(-anchor_x, anchor_bottom);
|
||||
glEnd();
|
||||
|
||||
c->texture->release();
|
||||
} else if (c->open) {
|
||||
close_clip(c);
|
||||
}
|
||||
} else if (render_audio &&
|
||||
c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
|
||||
c->lock.tryLock()) {
|
||||
// clip is not caching, start caching audio
|
||||
cache_clip(c, panel_timeline->playhead, false, false, c->reset_audio);
|
||||
c->lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (panel_timeline->playing) {
|
||||
int adjusted_read_index = audio_ibuffer_read%audio_ibuffer_size;
|
||||
int max_write = audio_ibuffer_size - adjusted_read_index;
|
||||
int actual_write = audio_io_device->write((const char*) audio_ibuffer+adjusted_read_index, max_write);
|
||||
memset(audio_ibuffer+adjusted_read_index, 0, actual_write);
|
||||
audio_ibuffer_read += actual_write;
|
||||
if (actual_write == max_write) {
|
||||
// got all the bytes, write again
|
||||
actual_write = audio_io_device->write((const char*) audio_ibuffer, audio_ibuffer_size);
|
||||
memset(audio_ibuffer, 0, actual_write);
|
||||
bool render_audio = (panel_timeline->playing || force_audio);
|
||||
|
||||
for (int i=0;i<current_clips.size();i++) {
|
||||
Clip* c = current_clips.at(i);
|
||||
|
||||
if (!c->finished_opening) {
|
||||
qDebug() << "[WARNING] Tried to display clip" << i << "but it's closed";
|
||||
texture_failed = true;
|
||||
} else if (is_clip_active(c, panel_timeline->playhead)) {
|
||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
// start preparing cache
|
||||
get_clip_frame(c, panel_timeline->playhead);
|
||||
|
||||
if (c->texture == NULL) {
|
||||
qDebug() << "[WARNING] Texture hasn't been created yet";
|
||||
texture_failed = true;
|
||||
} else if (panel_timeline->playhead >= c->timeline_in) {
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
glLoadIdentity();
|
||||
int half_width = c->sequence->width/2;
|
||||
int half_height = c->sequence->height/2;
|
||||
if (flip) {
|
||||
glOrtho(-half_width, half_width, -half_height, half_height, -1, 1);
|
||||
} else {
|
||||
glOrtho(-half_width, half_width, half_height, -half_height, -1, 1);
|
||||
}
|
||||
int anchor_x = c->media_stream->video_width/2;
|
||||
int anchor_y = c->media_stream->video_height/2;
|
||||
|
||||
// perform all transform effects
|
||||
for (int j=0;j<c->effects.size();j++) {
|
||||
c->effects.at(j)->process_gl(&anchor_x, &anchor_y);
|
||||
}
|
||||
|
||||
if (c->opening_transition != NULL) {
|
||||
int transition_progress = panel_timeline->playhead-c->timeline_in;
|
||||
if (transition_progress < c->opening_transition->length) {
|
||||
c->opening_transition->process_transition((float)transition_progress/(float)c->opening_transition->length);
|
||||
}
|
||||
}
|
||||
if (c->closing_transition != NULL) {
|
||||
int transition_progress = c->closing_transition->length-(panel_timeline->playhead-c->timeline_in-c->getLength()+c->closing_transition->length);
|
||||
if (transition_progress < c->closing_transition->length) {
|
||||
c->closing_transition->process_transition((float)transition_progress/(float)c->closing_transition->length);
|
||||
}
|
||||
}
|
||||
|
||||
int anchor_right = c->media_stream->video_width - anchor_x;
|
||||
int anchor_bottom = c->media_stream->video_height - anchor_y;
|
||||
|
||||
c->texture->bind();
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0.0, 0.0);
|
||||
glVertex2f(-anchor_x, -anchor_y);
|
||||
glTexCoord2f(1.0, 0.0);
|
||||
glVertex2f(anchor_right, -anchor_y);
|
||||
glTexCoord2f(1.0, 1.0);
|
||||
glVertex2f(anchor_right, anchor_bottom);
|
||||
glTexCoord2f(0.0, 1.0);
|
||||
glVertex2f(-anchor_x, anchor_bottom);
|
||||
glEnd();
|
||||
|
||||
c->texture->release();
|
||||
}
|
||||
} else if (render_audio &&
|
||||
c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
|
||||
c->lock.tryLock()) {
|
||||
// clip is not caching, start caching audio
|
||||
cache_clip(c, panel_timeline->playhead, false, false, c->reset_audio);
|
||||
c->lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (panel_timeline->playing) {
|
||||
int adjusted_read_index = audio_ibuffer_read%audio_ibuffer_size;
|
||||
int max_write = audio_ibuffer_size - adjusted_read_index;
|
||||
int actual_write = audio_io_device->write((const char*) audio_ibuffer+adjusted_read_index, max_write);
|
||||
memset(audio_ibuffer+adjusted_read_index, 0, actual_write);
|
||||
audio_ibuffer_read += actual_write;
|
||||
if (actual_write == max_write) {
|
||||
// got all the bytes, write again
|
||||
actual_write = audio_io_device->write((const char*) audio_ibuffer, audio_ibuffer_size);
|
||||
memset(audio_ibuffer, 0, actual_write);
|
||||
audio_ibuffer_read += actual_write;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*QPainter p(this);
|
||||
p.drawText({0, 0, 200, 200}, "text!");
|
||||
p.end();*/
|
||||
/*QPainter p(this);
|
||||
p.drawText({0, 0, 200, 200}, "text!");
|
||||
p.end();*/
|
||||
|
||||
if (texture_failed) {
|
||||
if (multithreaded) {
|
||||
retry_timer.start();
|
||||
} else {
|
||||
paintGL();
|
||||
if (texture_failed) {
|
||||
if (multithreaded) {
|
||||
retry_timer.start();
|
||||
} else {
|
||||
qDebug() << "[INFO] Texture failed - looping";
|
||||
loop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -19,11 +19,12 @@ public:
|
||||
bool multithreaded;
|
||||
bool force_audio;
|
||||
bool enable_paint;
|
||||
bool flip;
|
||||
void paintGL();
|
||||
void initializeGL();
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void initializeGL();
|
||||
// void resizeGL(int w, int h);
|
||||
void paintGL();
|
||||
private:
|
||||
QTimer retry_timer;
|
||||
QVector<Clip*> current_clips;
|
||||
|
||||
Reference in New Issue
Block a user