added waveform support in footage viewer and fixed audio regression where sound stopped playing if it hit EOF
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -13,7 +13,7 @@ public:
|
||||
void adjust();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
|
||||
signals:
|
||||
|
||||
|
||||
+80
-54
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user