diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index c94c87de2..67752b479 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -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; } diff --git a/io/exportthread.cpp b/io/exportthread.cpp index a2cb4cf8f..5e2035f09 100644 --- a/io/exportthread.cpp +++ b/io/exportthread.cpp @@ -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); diff --git a/olive.pro.user b/olive.pro.user index 5c7c80e42..eff894ea0 100644 --- a/olive.pro.user +++ b/olive.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/playback/cacher.cpp b/playback/cacher.cpp index a7751cd8f..8b81e991e 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -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) { diff --git a/playback/playback.cpp b/playback/playback.cpp index d5bc17a15..cc0807d8d 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -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) { diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index f19c90215..2a6f633bb 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -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;iclip_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;iclip_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;jtrack < 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;ifinished_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;jeffects.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;jtrack < 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;ifinished_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;jeffects.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; + } } } } diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index 37879150a..d9885ae32 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -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 current_clips;