added waveform support in footage viewer and fixed audio regression where sound stopped playing if it hit EOF

This commit is contained in:
itsmattkc
2018-10-19 21:23:15 +11:00
parent 8b0b33f431
commit 84347a0988
15 changed files with 223 additions and 154 deletions
+4 -4
View File
@@ -322,10 +322,10 @@ void ExportDialog::render_thread_finished() {
panel_sequence_viewer->viewer_widget->initializeGL();
}
void ExportDialog::prep_ui_for_render(bool rendering) {
ui->pushButton->setEnabled(!rendering);
ui->pushButton_2->setEnabled(!rendering);
ui->renderCancel->setEnabled(rendering);
void ExportDialog::prep_ui_for_render(bool r) {
ui->pushButton->setEnabled(!r);
ui->pushButton_2->setEnabled(!r);
ui->renderCancel->setEnabled(r);
}
void ExportDialog::on_pushButton_clicked() {
+1 -1
View File
@@ -43,7 +43,7 @@ private:
QVector<int> format_acodecs;
ExportThread* et;
void prep_ui_for_render(bool rendering);
void prep_ui_for_render(bool r);
bool cancelled;
};
+2 -2
View File
@@ -341,7 +341,7 @@ void ExportThread::run() {
QOpenGLFramebufferObject fbo(sequence->width, sequence->height, QOpenGLFramebufferObject::CombinedDepthStencil, GL_TEXTURE_RECTANGLE);
fbo.bind();
panel_sequence_viewer->viewer_widget->rendering = true;
rendering = true;
panel_sequence_viewer->viewer_widget->default_fbo = &fbo;
long file_audio_samples = 0;
@@ -393,7 +393,7 @@ void ExportThread::run() {
}
panel_sequence_viewer->viewer_widget->default_fbo = NULL;
panel_sequence_viewer->viewer_widget->rendering = false;
rendering = false;
fbo.release();
+18 -11
View File
@@ -23,14 +23,14 @@ extern "C" {
#include <QtMath>
#include <QAudioOutput>
#include <QPainter>
Viewer::Viewer(QWidget *parent) :
QDockWidget(parent),
ui(new Ui::Viewer),
seq(NULL),
playing(false),
created_sequence(false),
disable_viewer(false)
created_sequence(false)
{
ui->setupUi(this);
ui->headers->viewer = this;
@@ -81,9 +81,7 @@ void Viewer::reset_all_audio() {
for (int i=0;i<seq->clips.size();i++) {
Clip* c = seq->clips.at(i);
if (c != NULL) {
c->reset_audio();
}
if (c != NULL) c->reset_audio();
}
}
clear_audio_ibuffer();
@@ -199,17 +197,20 @@ void Viewer::toggle_play() {
}
void Viewer::play() {
if (panel_sequence_viewer->playing) panel_sequence_viewer->pause();
if (panel_footage_viewer->playing) panel_footage_viewer->pause();
if (seq != NULL) {
reset_all_audio();
if (audio_output->format().sampleRate() != seq->audio_frequency
|| audio_output->format().channelCount() != av_get_channel_layout_nb_channels(seq->audio_layout)) {
init_audio(seq);
}
reset_all_audio();
playhead_start = seq->playhead;
start_msecs = QDateTime::currentMSecsSinceEpoch();
playback_updater.start();
playing = true;
set_playpause_icon(false);
playback_updater.start();
audio_thread->notifyReceiver();
}
}
@@ -302,7 +303,6 @@ void Viewer::set_media(int type, void* media) {
seq->width = 1920;
seq->height = 1080;
seq->frame_rate = 30;
disable_viewer = true;
}
if (footage->audio_tracks.size() > 0) {
@@ -319,6 +319,13 @@ void Viewer::set_media(int type, void* media) {
c->clip_in = 0;
c->recalculateMaxLength();
seq->clips.append(c);
if (footage->video_tracks.size() == 0) {
viewer_widget->waveform = true;
viewer_widget->waveform_clip = c;
viewer_widget->waveform_ms = audio_stream;
viewer_widget->update();
}
} else {
seq->audio_frequency = 48000;
}
@@ -364,7 +371,7 @@ void Viewer::timer_update() {
}
void Viewer::clean_created_seq() {
disable_viewer = false;
viewer_widget->waveform = false;
if (created_sequence) {
// TODO delete undo commands referencing this sequence to avoid crashes
@@ -388,8 +395,8 @@ void Viewer::set_sequence(bool main, Sequence *s) {
ui->headers->setEnabled(!null_sequence);
ui->currentTimecode->setEnabled(!null_sequence);
ui->openGLWidget->setEnabled(!null_sequence && !disable_viewer);
ui->openGLWidget->setVisible(!null_sequence && !disable_viewer);
ui->openGLWidget->setEnabled(!null_sequence);
ui->openGLWidget->setVisible(!null_sequence);
ui->pushButton->setEnabled(!null_sequence);
ui->pushButton_2->setEnabled(!null_sequence);
ui->pushButton_3->setEnabled(!null_sequence);
-1
View File
@@ -74,7 +74,6 @@ private:
void set_sequence(bool main, Sequence* s);
bool main_sequence;
bool created_sequence;
bool disable_viewer;
};
#endif // VIEWER_H
+2 -2
View File
@@ -137,7 +137,7 @@ int AudioSenderThread::send_audio_to_output(int offset, int max) {
// send samples to audio monitor cache
// TODO make this work for the footage viewer
Sequence* s = NULL;
/*Sequence* s = NULL;
if (panel_footage_viewer->playing) {
s = panel_footage_viewer->seq;
}
@@ -169,7 +169,7 @@ int AudioSenderThread::send_audio_to_output(int offset, int max) {
panel_timeline->ui->audio_monitor->sample_cache.append(samples);
buffer_offset = next_buffer_offset;
}
}
}*/
memset(audio_ibuffer+offset, 0, actual_write);
+56 -21
View File
@@ -121,13 +121,13 @@ void cache_audio_worker(Clip* c, Clip* nest) {
}
} else {
if (ret == AVERROR_EOF) {
#ifdef AUDIOWARNINGS
qDebug() << "reached EOF while reading";
#endif
// TODO likewise, I'm not sure if this if statement breaks anything (see equivalent section in cache_video_worker)
if (!c->reverse) {
c->reached_end = true;
} else {
#ifdef AUDIOWARNINGS
qDebug() << "reached EOF";
#endif
}
} else {
qDebug() << "[WARNING] Raw audio frame data could not be retrieved." << ret;
@@ -144,7 +144,7 @@ void cache_audio_worker(Clip* c, Clip* nest) {
break;
} else {
#ifdef AUDIOWARNINGS
qDebug() << "reached EOF";
qDebug() << "reached EOF while pulling from filtergraph";
#endif
if (!c->reverse) break;
}
@@ -172,7 +172,7 @@ void cache_audio_worker(Clip* c, Clip* nest) {
(frame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format)) * frame->channels)
);
#ifdef AUDIOWARNINGS
qDebug() << "pts:" << c->frame->pts << "dur:" << c->frame->pkt_duration << "rev_target:" << c->rev_target << "offset:" << offset << "limit:" << rev_frame->linesize[0];
qDebug() << "pts:" << c->frame->pts << "dur:" << c->frame->pkt_duration << "rev_target:" << c->reverse_target << "offset:" << offset << "limit:" << rev_frame->linesize[0];
#endif
}
@@ -189,7 +189,7 @@ void cache_audio_worker(Clip* c, Clip* nest) {
}*/
#ifdef AUDIOWARNINGS
qDebug() << "pre cutoff deets::: rev_frame.pts:" << rev_frame->pts << "rev_frame.nb_samples" << rev_frame->nb_samples << "rev_target:" << c->rev_target;
qDebug() << "pre cutoff deets::: rev_frame.pts:" << rev_frame->pts << "rev_frame.nb_samples" << rev_frame->nb_samples << "rev_target:" << c->reverse_target;
#endif
rev_frame->nb_samples = qRound(static_cast<double>(c->reverse_target - rev_frame->pts) / c->stream->codecpar->sample_rate * c->sequence->audio_frequency);
#ifdef AUDIOWARNINGS
@@ -252,7 +252,7 @@ void cache_audio_worker(Clip* c, Clip* nest) {
int nb_samples = qRound((target_sts - frame_sts)*c->sequence->audio_frequency);
c->frame_sample_index = nb_samples * 4;
#ifdef AUDIOWARNINGS
qDebug() << "fsts:" << frame_sts << "tsts:" << target_sts << "nbs:" << nb_samples << "nbb:" << nb_bytes << "rev_targetToSec:" << (c->rev_target * timebase);
qDebug() << "fsts:" << frame_sts << "tsts:" << target_sts << "nbs:" << nb_samples << "nbb:" << nb_bytes << "rev_targetToSec:" << (c->reverse_target * timebase);
qDebug() << "fsi-calc:" << c->frame_sample_index;
#endif
if (c->reverse) c->frame_sample_index = nb_bytes - c->frame_sample_index;
@@ -262,7 +262,6 @@ void cache_audio_worker(Clip* c, Clip* nest) {
#ifdef AUDIOWARNINGS
qDebug() << "fsi-post-post:" << c->frame_sample_index;
#endif
if (c->audio_buffer_write == 0) c->audio_buffer_write = get_buffer_offset_from_frame(c->sequence, qMax(timeline_in, c->audio_target_frame));
int offset = (audio_ibuffer_read + 2048) - c->audio_buffer_write;
@@ -300,6 +299,7 @@ void cache_audio_worker(Clip* c, Clip* nest) {
apply_audio_effects(c, bytes_to_seconds(frame->pts, frame->channels, frame->sample_rate), frame, nb_bytes);
c->frame->pts += nb_bytes;
c->frame_sample_index = 0;
qDebug() << "2 >> c->audio_target_frame:" << c->audio_target_frame;
if (c->audio_buffer_write == 0) c->audio_buffer_write = get_buffer_offset_from_frame(c->sequence, qMax(timeline_in, c->audio_target_frame));
int offset = audio_ibuffer_read - c->audio_buffer_write;
if (offset > 0) {
@@ -354,11 +354,9 @@ void cache_audio_worker(Clip* c, Clip* nest) {
}
}
void cache_video_worker(Clip* c) {
void cache_video_worker(Clip* c, long playhead) {
int read_ret, send_ret, retr_ret;
// SKIP TYPE SEEK
int limit = c->max_queue_size;
if (c->reverse) limit *= 2;
@@ -372,7 +370,7 @@ void cache_video_worker(Clip* c) {
avcodec_flush_buffers(c->codecCtx);
av_seek_frame(c->formatCtx, c->stream->index, qMax(static_cast<int64_t>(0), smallest_pts - quarter_sec), AVSEEK_FLAG_BACKWARD);
} else {
smallest_pts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, c->cacher->playhead));
smallest_pts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, playhead));
}
while (true) {
@@ -383,6 +381,8 @@ void cache_video_worker(Clip* c) {
read_ret = (c->use_existing_frame) ? 0 : retrieve_next_frame(c, send_frame);
c->use_existing_frame = false;
if (read_ret >= 0) {
// sending NULL to avfiltergraph is supposed to flush it but it'll also no longer accept frames - particularly unhelpful when reversing
// we'll see if it still works completely without doing so
/*if (read_ret == AVERROR_EOF) send_frame = NULL;
if ((send_ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, send_frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
qDebug() << "[ERROR] Failed to add frame to buffer source." << send_ret;
@@ -391,6 +391,7 @@ void cache_video_worker(Clip* c) {
if (read_ret >= 0) {
av_frame_unref(c->frame);
}*/
if ((send_ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, send_frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
qDebug() << "[ERROR] Failed to add frame to buffer source." << send_ret;
break;
@@ -407,7 +408,7 @@ void cache_video_worker(Clip* c) {
av_frame_free(&frame);
break;
} else {
if (c->reverse && smallest_pts != INT64_MAX && frame->pts >= smallest_pts) {
if (c->reverse && frame->pts >= smallest_pts) {
av_frame_free(&frame);
break;
} else {
@@ -416,9 +417,38 @@ void cache_video_worker(Clip* c) {
c->queue.append(frame);
c->queue_lock.unlock();
qDebug() << "appended" << frame->pts << "/ min:" << smallest_pts;
if ((!c->reverse || smallest_pts == INT64_MAX) && c->queue.size() == limit) break;
//break;
if (!c->reverse && c->queue.size() == limit) {
if (rendering) {
// see if we got the frame we needed (used for speed ups primarily)
bool found = false;
for (int i=0;i<c->queue.size();i++) {
// TODO/NOTE: this will not work on sped up AND reversed video
if (c->queue.at(i)->pts >= smallest_pts) {
found = true;
break;
}
}
if (found) {
break;
} else {
// remove earliest frame and store another
int earliest_frame = 0;
for (int i=1;i<c->queue.size();i++) {
// TODO/NOTE: this will not work on sped up AND reversed video
if (c->queue.at(i)->pts < c->queue.at(earliest_frame)->pts) {
earliest_frame = i;
}
}
c->queue_lock.lock();
av_frame_free(&c->queue[earliest_frame]);
c->queue.removeAt(earliest_frame);
c->queue_lock.unlock();
}
} else {
break;
}
}
}
}
}
@@ -456,7 +486,6 @@ void reset_cache(Clip* c, long target_frame) {
qDebug() << "[WARNING] Seeking terminated prematurely";
break;
}
qDebug() << "seek ts:" << seek_ts << "target ts:" << target_ts << "retrieved ts:" << c->frame->pts;
if (c->frame->pts <= target_ts) {
c->use_existing_frame = true;
break;
@@ -471,15 +500,21 @@ void reset_cache(Clip* c, long target_frame) {
}
}
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
// flush ffmpeg codecs
avcodec_flush_buffers(c->codecCtx);
// seek (target_frame represents timeline timecode in frames, not clip timecode)
int64_t timestamp = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame)); // TODO qRound here might lead to clicking? or might fix it... who knows
int64_t timestamp = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame));
if (c->reverse) {
c->reverse_target = timestamp;
timestamp -= av_q2d(av_inv_q(c->stream->time_base));
#ifdef AUDIOWARNINGS
qDebug() << "seeking to" << timestamp << "(originally" << c->rev_target << ")";
qDebug() << "seeking to" << timestamp << "(originally" << c->reverse_target << ")";
} else {
qDebug() << "reset called; seeking to" << timestamp;
#endif
}
}
av_seek_frame(c->formatCtx, ms->file_index, timestamp, AVSEEK_FLAG_BACKWARD);
c->audio_target_frame = target_frame;
c->frame_sample_index = -1;
@@ -718,7 +753,7 @@ void cache_clip_worker(Clip* clip, long playhead, bool reset, Clip* nest) {
switch (clip->media_type) {
case MEDIA_TYPE_FOOTAGE:
if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
cache_video_worker(clip);
cache_video_worker(clip, playhead);
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
cache_audio_worker(clip, nest);
}
+8 -13
View File
@@ -26,6 +26,7 @@ extern "C" {
#include <QOpenGLFramebufferObject>
bool texture_failed = false;
bool rendering = false;
void open_clip(Clip* clip, bool multithreaded) {
switch (clip->media_type) {
@@ -117,8 +118,6 @@ void get_clip_frame(Clip* c, long playhead) {
bool reset = false;
qDebug() << "QS:" << c->queue.size();
if (c->queue.size() > 0) {
if (ms->infinite_length) {
target_frame = c->queue.at(0);
@@ -140,14 +139,10 @@ void get_clip_frame(Clip* c, long playhead) {
target_frame = c->queue.at(closest_frame);
c->queue_lock.lock();
for (int i=0;i<c->queue.size();i++) {
if (c->queue.at(i)->pts != target_frame->pts) {
if ((c->queue.at(i)->pts < target_frame->pts) == (!c->reverse)) {
qDebug() << "removed frame whose pts was" << c->queue.at(i)->pts << "compared to target" << target_frame->pts;
av_frame_free(&c->queue[i]); // may be a little heavy for the UI thread?
c->queue.removeAt(i);
i--;
}
if (c->queue.at(i)->pts != target_frame->pts && (c->queue.at(i)->pts < target_frame->pts) == (!c->reverse)) {
av_frame_free(&c->queue[i]); // may be a little heavy for the UI thread?
c->queue.removeAt(i);
i--;
}
}
c->queue_lock.unlock();
@@ -166,9 +161,6 @@ void get_clip_frame(Clip* c, long playhead) {
reset = true;
}
// get more frames
cache_clip(c, playhead, reset, NULL);
if (target_frame == NULL || reset) {
// reset cache
texture_failed = true;
@@ -181,6 +173,9 @@ void get_clip_frame(Clip* c, long playhead) {
c->texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, target_frame->data[0]);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
// get more frames
cache_clip(c, playhead, reset, NULL);
}
}
+2 -1
View File
@@ -10,12 +10,13 @@ struct Sequence;
struct AVFrame;
extern bool texture_failed;
extern bool rendering;
void open_clip(Clip* clip, bool multithreaded);
void cache_clip(Clip* clip, long playhead, bool reset, Clip *nest);
void close_clip(Clip* clip);
void cache_audio_worker(Clip* c, bool write_A);
void cache_video_worker(Clip* c);
void cache_video_worker(Clip* c, long playhead);
void handle_media(Sequence* sequence, long playhead, bool multithreaded);
void reset_cache(Clip* c, long target_frame);
void get_clip_frame(Clip* c, long playhead);
+36 -38
View File
@@ -1777,7 +1777,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
}
}
void TimelineWidget::leaveEvent(QEvent *event) {
void TimelineWidget::leaveEvent(QEvent*) {
tooltip_timer.stop();
}
@@ -1785,6 +1785,37 @@ int color_brightness(int r, int g, int b) {
return (0.2126*r + 0.7152*g + 0.0722*b);
}
void draw_waveform(Clip* clip, MediaStream* ms, long media_length, QPainter *p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom) {
int divider = ms->audio_channels*2;
int channel_height = clip_rect.height()/ms->audio_channels;
for (int i=waveform_start;i<waveform_limit;i++) {
int waveform_index = qFloor((((clip->clip_in + ((double) i/zoom))/media_length) * ms->audio_preview.size())/divider)*divider;
if (clip->reverse) {
waveform_index = ms->audio_preview.size() - waveform_index - (ms->audio_channels * 2);
}
for (int j=0;j<ms->audio_channels;j++) {
int mid = (config.rectified_waveforms) ? clip_rect.top()+channel_height*(j+1) : clip_rect.top()+channel_height*j+(channel_height/2);
int offset = waveform_index+(j*2);
if ((offset + 1) < ms->audio_preview.size()) {
qint8 min = (double)ms->audio_preview.at(offset) / 128.0 * (channel_height/2);
qint8 max = (double)ms->audio_preview.at(offset+1) / 128.0 * (channel_height/2);
if (config.rectified_waveforms) {
p->drawLine(clip_rect.left()+i, mid, clip_rect.left()+i, mid - (max - min));
} else {
p->drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max);
}
}/* else {
qDebug() << "[WARNING] Tried to reach" << offset + 1 << ", limit:" << ms->audio_preview.size();
}*/
}
}
}
void TimelineWidget::paintEvent(QPaintEvent*) {
// Draw clips
if (sequence != NULL) {
@@ -1869,6 +1900,9 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
}
}
} else if (clip_rect.height() > TRACK_MIN_HEIGHT) {
// draw waveform
p.setPen(QColor(80, 80, 80));
int waveform_start = -qMin(clip_rect.x(), 0);
int waveform_limit = qMin(clip_rect.width(), getScreenPointFromFrame(panel_timeline->zoom, media_length - clip->clip_in));
@@ -1879,43 +1913,7 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
if (waveform_limit > 0) checkerboard_rect.setLeft(checkerboard_rect.left() + waveform_limit);
}
// draw waveform
p.setPen(QColor(80, 80, 80));
int divider = ms->audio_channels*2;
int channel_height = clip_rect.height()/ms->audio_channels;
for (int i=waveform_start;i<waveform_limit;i++) {
int waveform_index = qFloor((((clip->clip_in + ((double) i/panel_timeline->zoom))/media_length) * ms->audio_preview.size())/divider)*divider;
if (clip->reverse) {
waveform_index = ms->audio_preview.size() - waveform_index - (ms->audio_channels * 2);
}
int rectified_height = 0;
for (int j=0;j<ms->audio_channels;j++) {
int mid = clip_rect.top()+channel_height*j+(channel_height/2);
int offset = waveform_index+(j*2);
if ((offset + 1) < ms->audio_preview.size()) {
qint8 min = (double)ms->audio_preview.at(offset) / 128.0 * (channel_height/2);
qint8 max = (double)ms->audio_preview.at(offset+1) / 128.0 * (channel_height/2);
if (config.rectified_waveforms) {
rectified_height += (max - min);
} else {
p.drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max);
}
} else {
qDebug() << "[WARNING] Tried to reach" << offset + 1 << ", limit:" << ms->audio_preview.size();
}
}
if (config.rectified_waveforms) {
p.drawLine(clip_rect.left()+i, clip_rect.height(), clip_rect.left()+i, clip_rect.height() - rectified_height);
}
}
draw_waveform(clip, ms, media_length, &p, clip_rect, waveform_start, waveform_limit, panel_timeline->zoom);
}
}
if (draw_checkerboard) {
+3
View File
@@ -13,12 +13,15 @@
struct Sequence;
struct Clip;
struct MediaStream;
class Timeline;
class TimelineAction;
class QScrollBar;
class SetSelectionsCommand;
class QPainter;
bool same_sign(int a, int b);
void draw_waveform(Clip* clip, MediaStream* ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom);
class TimelineWidget : public QWidget {
Q_OBJECT
+5 -4
View File
@@ -4,10 +4,11 @@
#include <QResizeEvent>
// enforces aspect ratio
ViewerContainer::ViewerContainer(QWidget *parent) : QWidget(parent) {
child = NULL;
aspect_ratio = 1;
}
ViewerContainer::ViewerContainer(QWidget *parent) :
QWidget(parent),
child(NULL),
aspect_ratio(1)
{}
void ViewerContainer::adjust() {
if (child != NULL) {
+1 -1
View File
@@ -13,7 +13,7 @@ public:
void adjust();
protected:
void resizeEvent(QResizeEvent *event);
void resizeEvent(QResizeEvent *event);
signals:
+80 -54
View File
@@ -27,10 +27,12 @@ extern "C" {
}
ViewerWidget::ViewerWidget(QWidget *parent) :
QOpenGLWidget(parent),
rendering(false),
default_fbo(NULL)
QOpenGLWidget(parent),
default_fbo(NULL),
waveform(false)
{
setFocusPolicy(Qt::ClickFocus);
QSurfaceFormat format;
format.setDepthBufferSize(24);
setFormat(format);
@@ -175,62 +177,71 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) {
QVector<Clip*> current_clips;
long endFrame = LONG_MIN;
for (int i=0;i<s->clips.size();i++) {
Clip* c = s->clips.at(i);
// if clip starts within one second and/or hasn't finished yet
if (c != NULL && !(nest != NULL && !same_sign(c->track, nest->track))) {
bool clip_is_active = false;
if (c != NULL) {
endFrame = qMax(endFrame, c->timeline_out);
switch (c->media_type) {
case MEDIA_TYPE_FOOTAGE:
{
Media* m = static_cast<Media*>(c->media);
if (m->ready) {
MediaStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
if (ms != NULL && is_clip_active(c, 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, !rendering);
if (!(nest != NULL && !same_sign(c->track, nest->track))) {
bool clip_is_active = false;
switch (c->media_type) {
case MEDIA_TYPE_FOOTAGE:
{
Media* m = static_cast<Media*>(c->media);
if (m->ready) {
MediaStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
if (ms != NULL && is_clip_active(c, 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, !rendering);
}
clip_is_active = true;
} else if (c->open) {
close_clip(c);
}
} else {
texture_failed = true;
}
}
break;
case MEDIA_TYPE_SEQUENCE:
case MEDIA_TYPE_SOLID:
case MEDIA_TYPE_TONE:
if (is_clip_active(c, playhead)) {
if (!c->open) open_clip(c, !rendering);
clip_is_active = true;
} else if (c->open) {
close_clip(c);
}
} else {
texture_failed = true;
break;
}
}
break;
case MEDIA_TYPE_SEQUENCE:
case MEDIA_TYPE_SOLID:
case MEDIA_TYPE_TONE:
if (is_clip_active(c, playhead)) {
if (!c->open) open_clip(c, !rendering);
clip_is_active = true;
} else if (c->open) {
close_clip(c);
}
break;
}
if (clip_is_active) {
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);
}
}
}
if (clip_is_active) {
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);
}
}
}
}
}
if (viewer->playing && s->playhead == endFrame) {
viewer->pause();
}
int half_width = s->width/2;
int half_height = s->height/2;
if (rendering || nest != NULL) half_height = -half_height;
@@ -444,28 +455,43 @@ void ViewerWidget::paintGL() {
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
texture_failed = false;
texture_failed = false;
if (!rendering) retry_timer.stop();
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT);
// compose video preview
glClearColor(0, 0, 0, 0);
compose_sequence(NULL, (viewer->playing || rendering));
if (texture_failed) {
if (waveform) {
double waveform_zoom = (double) waveform_ms->audio_preview.size() / (double) width();
double timeline_zoom = (double) width() / (double) waveform_clip->timeline_out;
QPainter p(this);
p.setPen(QColor(0, 255, 0));
if (viewer->seq->using_workarea) {
p.fillRect(QRect(getScreenPointFromFrame(timeline_zoom, viewer->seq->workarea_in), 0, getScreenPointFromFrame(timeline_zoom, viewer->seq->workarea_out-viewer->seq->workarea_in), height()), QColor(255, 255, 255, 64));
}
draw_waveform(waveform_clip, waveform_ms, waveform_clip->timeline_out, &p, rect(), 0, width(), waveform_zoom);
p.setPen(Qt::red);
int playhead_x = getScreenPointFromFrame(timeline_zoom, viewer->seq->playhead);
p.drawLine(playhead_x, 0, playhead_x, height());
}
if (texture_failed) {
if (rendering) {
qDebug() << "[INFO] Texture failed - looping";
loop = true;
} else {
} else {
retry_timer.start();
}
}
}
}
if (config.show_title_safe_area && !rendering) {
drawTitleSafeArea();
}
if (config.show_title_safe_area && !rendering) {
drawTitleSafeArea();
}
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
+5 -1
View File
@@ -11,6 +11,7 @@
class Viewer;
struct Clip;
struct MediaStream;
class QOpenGLFramebufferObject;
class ViewerWidget : public QOpenGLWidget
@@ -19,11 +20,14 @@ class ViewerWidget : public QOpenGLWidget
public:
ViewerWidget(QWidget *parent = 0);
bool rendering;
void paintGL();
void initializeGL();
Viewer* viewer;
bool waveform;
Clip* waveform_clip;
MediaStream* waveform_ms;
QOpenGLFramebufferObject* default_fbo;
protected:
void paintEvent(QPaintEvent *e);